Updated hidapi to 0.14.0 release
Upstream: https://github.com/libusb/hidapi/releases/tag/hidapi-0.14.0
diff --git a/src/hidapi/.appveyor.yml b/src/hidapi/.appveyor.yml
new file mode 100644
index 0000000..210b3fa
--- /dev/null
+++ b/src/hidapi/.appveyor.yml
@@ -0,0 +1,31 @@
+environment:
+ matrix:
+ - BUILD_ENV: msbuild
+ arch: x64
+ - BUILD_ENV: msbuild
+ arch: Win32
+ - BUILD_ENV: cygwin
+
+for:
+ -
+ matrix:
+ only:
+ - BUILD_ENV: msbuild
+
+ os: Visual Studio 2015
+
+ build_script:
+ - cmd: msbuild .\windows\hidapi.sln /p:Configuration=Release /p:Platform=%arch% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
+
+ -
+ matrix:
+ only:
+ - BUILD_ENV: cygwin
+
+ os: Visual Studio 2022
+
+ install:
+ - cmd: C:\cygwin64\setup-x86_64.exe --quiet-mode --no-shortcuts --upgrade-also --packages autoconf,automake
+
+ build_script:
+ - cmd: C:\cygwin64\bin\bash -exlc "cd $APPVEYOR_BUILD_FOLDER; ./bootstrap; ./configure; make"
diff --git a/src/hidapi/.builds/freebsd.yml b/src/hidapi/.builds/freebsd.yml
new file mode 100644
index 0000000..1679b03
--- /dev/null
+++ b/src/hidapi/.builds/freebsd.yml
@@ -0,0 +1,34 @@
+image: freebsd/latest
+packages:
+- autoconf
+- automake
+- gmake
+- libiconv
+- libtool
+- pkgconf
+- cmake
+- ninja
+sources:
+- https://github.com/libusb/hidapi
+tasks:
+- configure: |
+ cd hidapi
+ echo Configure Autotools build
+ ./bootstrap
+ ./configure
+ echo Configure CMake build
+ mkdir -p build install_cmake
+ cmake -GNinja -B build -S . -DCMAKE_INSTALL_PREFIX=install_cmake
+- build-autotools: |
+ cd hidapi
+ make
+ make DESTDIR=$PWD/root install
+ make clean
+- build-cmake: |
+ cd hidapi/build
+ ninja
+ ninja install
+ ninja clean
+- build-manual: |
+ cd hidapi/libusb
+ gmake -f Makefile-manual
diff --git a/src/hidapi/.builds/netbsd.yml b/src/hidapi/.builds/netbsd.yml
new file mode 100644
index 0000000..413d91c
--- /dev/null
+++ b/src/hidapi/.builds/netbsd.yml
@@ -0,0 +1,18 @@
+image: netbsd/latest
+packages:
+- cmake
+- pkgconf
+- libusb1
+- libiconv
+sources:
+- https://github.com/libusb/hidapi
+tasks:
+- configure: |
+ cd hidapi
+ mkdir -p build install
+ cmake -B build -S . -DCMAKE_INSTALL_PREFIX=install
+- build: |
+ cd hidapi/build
+ make
+ make install
+ make clean
diff --git a/src/hidapi/.builds/openbsd.yml b/src/hidapi/.builds/openbsd.yml
new file mode 100644
index 0000000..780df7f
--- /dev/null
+++ b/src/hidapi/.builds/openbsd.yml
@@ -0,0 +1,19 @@
+image: openbsd/latest
+packages:
+- cmake
+- pkgconf
+- libusb1--
+- libiconv
+- ninja
+sources:
+- https://github.com/libusb/hidapi
+tasks:
+- configure: |
+ cd hidapi
+ mkdir -p build install
+ cmake -GNinja -B build -S . -DCMAKE_INSTALL_PREFIX=install
+- build: |
+ cd hidapi/build
+ ninja
+ ninja install
+ ninja clean
diff --git a/src/hidapi/.cirrus.yml b/src/hidapi/.cirrus.yml
new file mode 100644
index 0000000..b4cf201
--- /dev/null
+++ b/src/hidapi/.cirrus.yml
@@ -0,0 +1,33 @@
+alpine_task:
+ container:
+ image: alpine:latest
+ install_script: apk add autoconf automake g++ gcc libusb-dev libtool linux-headers eudev-dev make musl-dev
+ script:
+ - ./bootstrap
+ - ./configure || { cat config.log; exit 1; }
+ - make
+ - make install
+
+freebsd11_task:
+ freebsd_instance:
+ image: freebsd-11-2-release-amd64
+ install_script:
+ - pkg install -y
+ autoconf automake libiconv libtool pkgconf
+ script:
+ - ./bootstrap
+ - ./configure || { cat config.log; exit 1; }
+ - make
+ - make install
+
+freebsd12_task:
+ freebsd_instance:
+ image: freebsd-12-1-release-amd64
+ install_script:
+ - pkg install -y
+ autoconf automake libiconv libtool pkgconf
+ script:
+ - ./bootstrap
+ - ./configure || { cat config.log; exit 1; }
+ - make
+ - make install
diff --git a/src/hidapi/.gitattributes b/src/hidapi/.gitattributes
new file mode 100644
index 0000000..edb79fe
--- /dev/null
+++ b/src/hidapi/.gitattributes
@@ -0,0 +1,7 @@
+* text=auto
+
+*.sln text eol=crlf
+*.vcproj text eol=crlf
+
+bootstrap text eol=lf
+configure.ac text eol=lf
diff --git a/src/hidapi/.github/workflows/builds.yml b/src/hidapi/.github/workflows/builds.yml
new file mode 100644
index 0000000..056df2c
--- /dev/null
+++ b/src/hidapi/.github/workflows/builds.yml
@@ -0,0 +1,540 @@
+name: GitHub Builds
+
+on: [push, pull_request]
+
+env:
+ NIX_COMPILE_FLAGS: -Wall -Wextra -pedantic -Werror
+ MSVC_COMPILE_FLAGS: /W4 /WX
+
+jobs:
+ macos-automake:
+
+ runs-on: macos-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Install build tools
+ run: brew install autoconf automake libtool
+ - name: Configure Automake
+ run: |
+ ./bootstrap
+ ./configure --prefix=$(pwd)/install
+ - name: Build Automake
+ run: |
+ make
+ make install
+ - name: Clean build
+ run: make clean
+ - name: Build Manual makefile
+ working-directory: mac
+ run: make -f Makefile-manual
+
+
+ macos-cmake:
+
+ runs-on: macos-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ path: hidapisrc
+ - name: Install dependencies
+ run: brew install meson ninja
+ - name: Configure CMake
+ run: |
+ rm -rf build install
+ cmake -B build/shared -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install/shared -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ cmake -B build/static -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install/static -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ cmake -B build/framework -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install/framework -DCMAKE_FRAMEWORK=ON -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ - name: Build CMake Shared
+ working-directory: build/shared
+ run: make install
+ - name: Build CMake Static
+ working-directory: build/static
+ run: make install
+ - name: Build CMake Framework
+ working-directory: build/framework
+ run: make install
+ - name: Check artifacts
+ uses: andstor/file-existence-action@v2
+ with:
+ files: "install/shared/lib/libhidapi.dylib, \
+ install/shared/include/hidapi/hidapi.h, \
+ install/shared/include/hidapi/hidapi_darwin.h, \
+ install/static/lib/libhidapi.a, \
+ install/static/include/hidapi/hidapi.h, \
+ install/static/include/hidapi/hidapi_darwin.h, \
+ install/framework/lib/hidapi.framework/hidapi, \
+ install/framework/lib/hidapi.framework/Headers/hidapi.h, \
+ install/framework/lib/hidapi.framework/Headers/hidapi_darwin.h"
+ fail: true
+ - name: Check CMake Export Package Shared
+ run: |
+ cmake \
+ -B build/shared_test \
+ -S hidapisrc/hidtest \
+ -Dhidapi_ROOT=install/shared \
+ -DCMAKE_INSTALL_PREFIX=install/shared_test \
+ "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ cd build/shared_test
+ make install
+ - name: Check CMake Export Package Static
+ run: |
+ cmake \
+ -B build/static_test \
+ -S hidapisrc/hidtest \
+ -Dhidapi_ROOT=install/static \
+ -DCMAKE_INSTALL_PREFIX=install/static_test \
+ "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ cd build/static_test
+ make install
+
+ - name: Check Meson build
+ run: |
+ meson setup build_meson hidapisrc
+ cd build_meson
+ ninja
+
+
+ ubuntu-cmake:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ path: hidapisrc
+ - name: Install dependencies
+ run: |
+ sudo apt update
+ sudo apt install libudev-dev libusb-1.0-0-dev python3-pip ninja-build
+ sudo -H pip3 install meson
+ - name: Configure CMake
+ run: |
+ rm -rf build install
+ cmake -B build/shared -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install/shared -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ cmake -B build/static -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install/static -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ - name: Build CMake Shared
+ working-directory: build/shared
+ run: make install
+ - name: Build CMake Static
+ working-directory: build/static
+ run: make install
+ - name: Check artifacts
+ uses: andstor/file-existence-action@v2
+ with:
+ files: "install/shared/lib/libhidapi-libusb.so, \
+ install/shared/lib/libhidapi-hidraw.so, \
+ install/shared/include/hidapi/hidapi.h, \
+ install/shared/include/hidapi/hidapi_libusb.h, \
+ install/static/lib/libhidapi-libusb.a, \
+ install/static/lib/libhidapi-hidraw.a, \
+ install/static/include/hidapi/hidapi.h, \
+ install/static/include/hidapi/hidapi_libusb.h"
+ fail: true
+ - name: Check CMake Export Package Shared
+ run: |
+ cmake \
+ -B build/shared_test \
+ -S hidapisrc/hidtest \
+ -Dhidapi_ROOT=install/shared \
+ -DCMAKE_INSTALL_PREFIX=install/shared_test \
+ "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ cd build/shared_test
+ make install
+ - name: Check CMake Export Package Static
+ run: |
+ cmake \
+ -B build/static_test \
+ -S hidapisrc/hidtest \
+ -Dhidapi_ROOT=install/static \
+ -DCMAKE_INSTALL_PREFIX=install/static_test \
+ "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ cd build/static_test
+ make install
+
+ - name: Check Meson build
+ run: |
+ meson setup build_meson hidapisrc
+ cd build_meson
+ ninja
+
+
+ windows-cmake:
+
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ path: hidapisrc
+ - name: Install dependencies
+ run: |
+ choco install ninja
+ pip3 install meson
+ refreshenv
+ - name: Configure CMake MSVC
+ shell: cmd
+ run: |
+ cmake -B build\msvc -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_PP_DATA_DUMP=ON -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install\msvc -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=%MSVC_COMPILE_FLAGS%"
+ - name: Build CMake MSVC
+ working-directory: build/msvc
+ run: cmake --build . --config RelWithDebInfo --target install
+ - name: Check artifacts MSVC
+ uses: andstor/file-existence-action@v2
+ with:
+ files: "install/msvc/lib/hidapi.lib, \
+ install/msvc/bin/hidapi.dll, \
+ install/msvc/include/hidapi/hidapi.h, \
+ install/msvc/include/hidapi/hidapi_winapi.h"
+ fail: true
+ - name: Check CMake Export Package
+ shell: cmd
+ run: |
+ cmake ^
+ -B build\msvc_test ^
+ -S hidapisrc\hidtest ^
+ -Dhidapi_ROOT=install\msvc ^
+ -DCMAKE_INSTALL_PREFIX=install\msvc_test ^
+ "-DCMAKE_C_FLAGS=%MSVC_COMPILE_FLAGS%"
+ cd build\msvc_test
+ cmake --build . --target install
+ - name: Run CTest MSVC
+ shell: cmd
+ working-directory: build/msvc
+ run: ctest -C RelWithDebInfo --rerun-failed --output-on-failure
+
+ - name: Configure CMake NMake
+ shell: cmd
+ run: |
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ cmake -G"NMake Makefiles" -B build\nmake -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_PP_DATA_DUMP=ON -DHIDAPI_ENABLE_ASAN=ON -DCMAKE_INSTALL_PREFIX=install\nmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=%MSVC_COMPILE_FLAGS%"
+ - name: Build CMake NMake
+ working-directory: build\nmake
+ shell: cmd
+ run: |
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ nmake install
+ - name: Check artifacts NMake
+ uses: andstor/file-existence-action@v2
+ with:
+ files: "install/nmake/lib/hidapi.lib, \
+ install/nmake/bin/hidapi.dll, \
+ install/nmake/include/hidapi/hidapi.h, \
+ install/nmake/include/hidapi/hidapi_winapi.h"
+ fail: true
+ - name: Check CMake Export Package NMake
+ shell: cmd
+ run: |
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ cmake ^
+ -G"NMake Makefiles" ^
+ -B build\nmake_test ^
+ -S hidapisrc\hidtest ^
+ -Dhidapi_ROOT=install\nmake ^
+ -DCMAKE_INSTALL_PREFIX=install\nmake_test ^
+ "-DCMAKE_C_FLAGS=%MSVC_COMPILE_FLAGS%"
+ cd build\nmake_test
+ nmake install
+ - name: Run CTest NMake
+ working-directory: build\nmake
+ run: ctest --rerun-failed --output-on-failure
+
+ - name: Configure CMake MinGW
+ shell: cmd
+ run: |
+ cmake -G"MinGW Makefiles" -B build\mingw -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_PP_DATA_DUMP=ON -DCMAKE_INSTALL_PREFIX=install\mingw -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=%NIX_COMPILE_FLAGS%"
+ - name: Build CMake MinGW
+ working-directory: build\mingw
+ run: cmake --build . --target install
+ - name: Check artifacts MinGW
+ uses: andstor/file-existence-action@v2
+ with:
+ files: "install/mingw/lib/libhidapi.dll.a, \
+ install/mingw/bin/libhidapi.dll, \
+ install/mingw/include/hidapi/hidapi.h, \
+ install/mingw/include/hidapi/hidapi_winapi.h"
+ fail: true
+ - name: Check CMake Export Package MinGW
+ shell: cmd
+ run: |
+ cmake ^
+ -G"MinGW Makefiles" ^
+ -B build\mingw_test ^
+ -S hidapisrc\hidtest ^
+ -Dhidapi_ROOT=install\mingw ^
+ -DCMAKE_INSTALL_PREFIX=install\mingw_test ^
+ "-DCMAKE_C_FLAGS=%NIX_COMPILE_FLAGS%"
+ cd build\mingw_test
+ cmake --build . --target install
+ - name: Run CTest MinGW
+ working-directory: build\mingw
+ run: ctest --rerun-failed --output-on-failure
+
+ - name: Check Meson build
+ shell: cmd
+ run: |
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ meson setup build_meson hidapisrc
+ cd build_meson
+ ninja
+
+
+ windows-msbuild:
+
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - uses: microsoft/setup-msbuild@v1.1
+ - name: MSBuild x86
+ run: msbuild windows\hidapi.sln /p:Configuration=Release /p:Platform=Win32
+ - name: Check artifacts x86
+ uses: andstor/file-existence-action@v2
+ with:
+ files: "windows/Release/hidapi.dll, windows/Release/hidapi.lib, windows/Release/hidapi.pdb"
+ fail: true
+ - name: MSBuild x64
+ run: msbuild windows\hidapi.sln /p:Configuration=Release /p:Platform=x64
+ - name: Check artifacts x64
+ uses: andstor/file-existence-action@v2
+ with:
+ files: "windows/x64/Release/hidapi.dll, windows/x64/Release/hidapi.lib, windows/x64/Release/hidapi.pdb"
+ fail: true
+ - name: Gather artifacts
+ run: |
+ md artifacts
+ md artifacts\x86
+ md artifacts\x64
+ md artifacts\include
+ Copy-Item "windows\Release\hidapi.dll","windows\Release\hidapi.lib","windows\Release\hidapi.pdb" -Destination "artifacts\x86"
+ Copy-Item "windows\x64\Release\hidapi.dll","windows\x64\Release\hidapi.lib","windows\x64\Release\hidapi.pdb" -Destination "artifacts\x64"
+ Copy-Item "hidapi\hidapi.h","windows\hidapi_winapi.h" -Destination "artifacts\include"
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: hidapi-win
+ path: artifacts/
+ retention-days: ${{ (github.event_name == 'pull_request' || github.ref_name != 'master') && 7 || 90 }}
+
+
+ fedora-mingw:
+
+ runs-on: ubuntu-latest
+ container: fedora:latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ path: hidapisrc
+ - name: Install dependencies
+ run: sudo dnf install -y autoconf automake libtool mingw64-gcc cmake ninja-build make
+ - name: Configure CMake
+ run: |
+ rm -rf build install
+ mingw64-cmake -B build/shared-cmake -S hidapisrc -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/shared-cmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ mingw64-cmake -B build/static-cmake -S hidapisrc -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/static-cmake -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ - name: Configure Automake
+ working-directory: hidapisrc
+ run: |
+ ./bootstrap
+ mingw64-configure
+ - name: Build CMake Shared
+ working-directory: build/shared-cmake
+ run: ninja install
+ - name: Build CMake Static
+ working-directory: build/static-cmake
+ run: ninja install
+ - name: Build Automake
+ working-directory: hidapisrc
+ run: |
+ make
+ make DESTDIR=$PWD/../install/automake install
+ make clean
+ - name: Build manual Makefile
+ working-directory: hidapisrc/windows
+ run: make -f Makefile-manual OS=MINGW CC=x86_64-w64-mingw32-gcc
+ - name: Check artifacts
+ uses: andstor/file-existence-action@v2
+ with:
+ files: "install/shared-cmake/bin/libhidapi.dll, \
+ install/shared-cmake/lib/libhidapi.dll.a, \
+ install/shared-cmake/include/hidapi/hidapi.h, \
+ install/shared-cmake/include/hidapi/hidapi_winapi.h, \
+ install/static-cmake/lib/libhidapi.a, \
+ install/static-cmake/include/hidapi/hidapi.h, \
+ install/static-cmake/include/hidapi/hidapi_winapi.h"
+ fail: true
+ - name: Check CMake Export Package Shared
+ run: |
+ mingw64-cmake \
+ -GNinja \
+ -B build/shared_test \
+ -S hidapisrc/hidtest \
+ -Dhidapi_DIR=$PWD/install/shared-cmake/lib/cmake/hidapi \
+ -DCMAKE_INSTALL_PREFIX=install/shared_test \
+ "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ cd build/shared_test
+ ninja install
+ - name: Check CMake Export Package Static
+ run: |
+ mingw64-cmake \
+ -GNinja \
+ -B build/static_test \
+ -S hidapisrc/hidtest \
+ -Dhidapi_DIR=$PWD/install/static-cmake/lib/cmake/hidapi \
+ -DCMAKE_INSTALL_PREFIX=install/static_test \
+ "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ cd build/static_test
+ ninja install
+
+
+ archlinux:
+
+ runs-on: ubuntu-latest
+ container: archlinux:latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ path: hidapisrc
+ - name: Install dependencies
+ run: |
+ pacman -Sy
+ pacman -S --noconfirm gcc pkg-config autoconf automake libtool libusb libudev0 cmake make
+ - name: Configure CMake
+ run: |
+ rm -rf build install
+ cmake -B build/shared-cmake -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/shared-cmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ cmake -B build/static-cmake -S hidapisrc -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/static-cmake -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ - name: Configure Automake
+ working-directory: hidapisrc
+ run: |
+ ./bootstrap
+ ./configure
+ - name: Build CMake Shared
+ working-directory: build/shared-cmake
+ run: make install
+ - name: Build CMake Static
+ working-directory: build/static-cmake
+ run: make install
+ - name: Build Automake
+ working-directory: hidapisrc
+ run: |
+ make
+ make DESTDIR=$PWD/../install/automake install
+ make clean
+ - name: Build manual Makefile
+ run: |
+ cd hidapisrc/linux
+ make -f Makefile-manual
+ cd ../libusb
+ make -f Makefile-manual
+ - name: Check artifacts
+ uses: andstor/file-existence-action@v2
+ with:
+ files: "install/shared-cmake/lib/libhidapi-libusb.so, \
+ install/shared-cmake/lib/libhidapi-hidraw.so, \
+ install/shared-cmake/include/hidapi/hidapi.h, \
+ install/shared-cmake/include/hidapi/hidapi_libusb.h, \
+ install/static-cmake/lib/libhidapi-libusb.a, \
+ install/static-cmake/lib/libhidapi-hidraw.a, \
+ install/static-cmake/include/hidapi/hidapi.h, \
+ install/static-cmake/include/hidapi/hidapi_libusb.h"
+ fail: true
+ - name: Check CMake Export Package Shared
+ run: |
+ cmake \
+ -B build/shared_test \
+ -S hidapisrc/hidtest \
+ -Dhidapi_ROOT=install/shared-cmake \
+ -DCMAKE_INSTALL_PREFIX=install/shared_test \
+ "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ cd build/shared_test
+ make install
+ - name: Check CMake Export Package Static
+ run: |
+ cmake \
+ -B build/static_test \
+ -S hidapisrc/hidtest \
+ -Dhidapi_ROOT=install/static-cmake \
+ -DCMAKE_INSTALL_PREFIX=install/static_test \
+ "-DCMAKE_C_FLAGS=${NIX_COMPILE_FLAGS}"
+ cd build/static_test
+ make install
+
+
+ alpine:
+
+ runs-on: ubuntu-latest
+ container: alpine:edge
+ env:
+ # A bug in musl: https://www.openwall.com/lists/musl/2020/01/20/2
+ ALPINE_COMPILE_FLAGS: ${NIX_COMPILE_FLAGS} -Wno-overflow
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ path: hidapisrc
+ - name: Install dependencies
+ run: |
+ apk add gcc musl-dev autoconf automake libtool eudev-dev libusb-dev linux-headers cmake ninja make
+ - name: Configure CMake
+ run: |
+ rm -rf build install
+ cmake -B build/shared-cmake -S hidapisrc -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/shared-cmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${ALPINE_COMPILE_FLAGS}"
+ cmake -B build/static-cmake -S hidapisrc -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=install/static-cmake -DBUILD_SHARED_LIBS=FALSE -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=${ALPINE_COMPILE_FLAGS}"
+ - name: Configure Automake
+ working-directory: hidapisrc
+ run: |
+ ./bootstrap
+ ./configure
+ - name: Build CMake Shared
+ working-directory: build/shared-cmake
+ run: ninja install
+ - name: Build CMake Static
+ working-directory: build/static-cmake
+ run: ninja install
+ - name: Build Automake
+ working-directory: hidapisrc
+ run: |
+ make
+ make DESTDIR=$PWD/../install/automake install
+ make clean
+ - name: Build manual Makefile
+ run: |
+ cd hidapisrc/linux
+ make -f Makefile-manual
+ cd ../libusb
+ make -f Makefile-manual
+ - name: Check artifacts
+ uses: andstor/file-existence-action@v2
+ with:
+ files: "install/shared-cmake/lib/libhidapi-libusb.so, \
+ install/shared-cmake/lib/libhidapi-hidraw.so, \
+ install/shared-cmake/include/hidapi/hidapi.h, \
+ install/shared-cmake/include/hidapi/hidapi_libusb.h, \
+ install/static-cmake/lib/libhidapi-libusb.a, \
+ install/static-cmake/lib/libhidapi-hidraw.a, \
+ install/static-cmake/include/hidapi/hidapi.h, \
+ install/static-cmake/include/hidapi/hidapi_libusb.h"
+ fail: true
+ - name: Check CMake Export Package Shared
+ run: |
+ cmake \
+ -GNinja \
+ -B build/shared_test \
+ -S hidapisrc/hidtest \
+ -Dhidapi_ROOT=install/shared-cmake \
+ -DCMAKE_INSTALL_PREFIX=install/shared_test \
+ "-DCMAKE_C_FLAGS=${ALPINE_COMPILE_FLAGS}"
+ cd build/shared_test
+ ninja install
+ - name: Check CMake Export Package Static
+ run: |
+ cmake \
+ -GNinja \
+ -B build/static_test \
+ -S hidapisrc/hidtest \
+ -Dhidapi_ROOT=install/static-cmake \
+ -DCMAKE_INSTALL_PREFIX=install/static_test \
+ "-DCMAKE_C_FLAGS=${ALPINE_COMPILE_FLAGS}"
+ cd build/static_test
+ ninja install
diff --git a/src/hidapi/.github/workflows/checks.yml b/src/hidapi/.github/workflows/checks.yml
new file mode 100644
index 0000000..e03dc66
--- /dev/null
+++ b/src/hidapi/.github/workflows/checks.yml
@@ -0,0 +1,196 @@
+name: Checks
+run-name: Code checks for '${{ github.ref_name }}'
+
+# General comment:
+# Coverity doesn't support merging or including reports from multible machine/platforms (at least not officially).
+# But otherwise there is no good way to keep the issues from all platforms at Coverity Scans at once.
+# This script uses undocumented (but appears to be working) hack:
+# The build logs from one machine/platform gets moved to a next once,
+# and "fixed" so that cov-build can append logs from the next platform.
+# The "fix" is based on the fact, that Coverity perfectly allows appending logs from multiple builds
+# that are done *on the same host* machine.
+
+on:
+ # On-demand run
+ workflow_dispatch:
+ # Weekly run
+ schedule:
+ - cron: '30 5 * * 0'
+
+jobs:
+ coverity-windows:
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ path: src
+ - name: Setup MSVC
+ uses: TheMrMilchmann/setup-msvc-dev@v2.0.0
+ with:
+ arch: x64
+ - name: Configure
+ run: |
+ cmake -B build -S src -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_HIDTEST=ON
+ - name: Lookup Coverity Build Tool hash
+ id: coverity-cache-lookup
+ run: |
+ $coverity_hash=Invoke-RestMethod -Uri https://scan.coverity.com/download/cxx/win64 -Method Post -Body @{token='${{ secrets.COVERITY_SCAN_TOKEN }}';project='hidapi';md5=1}
+ echo "coverity_hash=$coverity_hash" >> $Env:GITHUB_OUTPUT
+ - name: Get cached Coverity Build Tool
+ id: cov-build-cache
+ uses: actions/cache@v3
+ with:
+ path: cov-root
+ key: cov-root-cxx-win64-${{ steps.coverity-cache-lookup.outputs.coverity_hash }}
+ - name: Get and configure Coverity
+ if: steps.cov-build-cache.outputs.cache-hit != 'true'
+ run: |
+ Invoke-WebRequest -Uri https://scan.coverity.com/download/cxx/win64 -OutFile coverity.zip -Method Post -Body @{token='${{ secrets.COVERITY_SCAN_TOKEN }}';project='hidapi'}
+ Remove-Item 'cov-root' -Recurse -Force -ErrorAction SilentlyContinue
+ Expand-Archive coverity.zip -DestinationPath cov-root
+
+ $cov_root=Get-ChildItem -Path 'cov-root'
+ $Env:PATH += ";$($cov_root.FullName)\bin"
+ cov-configure -msvc
+ - name: Make Coverity available in PATH
+ run: |
+ $cov_root=Get-ChildItem -Path 'cov-root'
+ echo "$($cov_root.FullName)\bin" >> $Env:GITHUB_PATH
+ - name: Build with Coverity
+ working-directory: build
+ run: |
+ cov-build --dir cov-int nmake
+ Rename-Item ".\cov-int\emit\$(hostname)" hostname
+ - name: Backup Coverity logs
+ uses: actions/upload-artifact@v3
+ with:
+ name: coverity-logs-windows
+ path: build/cov-int
+ retention-days: 7
+
+
+ coverity-macos:
+ runs-on: macos-latest
+ needs: [coverity-windows]
+
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ path: src
+ - name: Install dependencies
+ run: brew install ninja
+ - name: Configure
+ run: |
+ cmake -B build -S src -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_HIDTEST=ON -DCMAKE_C_COMPILER=clang
+ - uses: actions/download-artifact@v3
+ with:
+ name: coverity-logs-windows
+ path: build/cov-int
+ - name: Fixup cov-int
+ run: |
+ rm -f build/cov-int/emit/hostname/emit-db.lock build/cov-int/emit/hostname/emit-db.write-lock
+ mv build/cov-int/emit/hostname build/cov-int/emit/$(hostname)
+ - name: Lookup Coverity Build Tool hash
+ id: coverity-cache-lookup
+ shell: bash
+ run: |
+ hash=$(curl https://scan.coverity.com/download/cxx/Darwin --data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=hidapi&md5=1")
+ echo "coverity_hash=${hash}" >> $GITHUB_OUTPUT
+ - name: Get cached Coverity Build Tool
+ id: cov-build-cache
+ uses: actions/cache@v3
+ with:
+ path: cov-root
+ key: cov-root-cxx-Darwin-${{ steps.coverity-cache-lookup.outputs.coverity_hash }}
+ - name: Get and configure Coverity
+ if: steps.cov-build-cache.outputs.cache-hit != 'true'
+ run: |
+ curl https://scan.coverity.com/download/cxx/Darwin --output coverity.dmg --data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=hidapi"
+ hdiutil attach coverity.dmg -mountroot coverity
+ export COV_DIR_NAME=$(ls -1 --color=never coverity)
+ rm -rf cov-root
+ mkdir cov-root
+ cp ./coverity/${COV_DIR_NAME}/${COV_DIR_NAME}.sh cov-root/
+ cd cov-root/
+ ./${COV_DIR_NAME}.sh
+ ./bin/cov-configure --clang
+ - name: Make Coverity available in PATH
+ run: echo "$(pwd)/cov-root/bin" >> $GITHUB_PATH
+ - name: Build with Coverity
+ working-directory: build
+ run: |
+ cov-build --dir cov-int --append-log ninja
+ mv cov-int/emit/$(hostname) cov-int/emit/hostname
+ - name: Backup Coverity logs
+ uses: actions/upload-artifact@v3
+ with:
+ name: coverity-logs-windows-macos
+ path: build/cov-int
+ retention-days: 7
+
+
+ coverity-ubuntu:
+ runs-on: ubuntu-latest
+ needs: [coverity-macos]
+
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ path: src
+ - name: Install dependencies
+ run: sudo apt install libudev-dev libusb-1.0-0-dev ninja-build
+ - name: Configure
+ run: |
+ cmake -B build -S src -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHIDAPI_WITH_TESTS=ON -DHIDAPI_BUILD_HIDTEST=ON -DCMAKE_C_COMPILER=gcc
+ - uses: actions/download-artifact@v3
+ with:
+ name: coverity-logs-windows-macos
+ path: build/cov-int
+ - name: Fixup cov-int
+ run: |
+ rm -f build/cov-int/emit/hostname/emit-db.lock build/cov-int/emit/hostname/emit-db.write-lock
+ mv build/cov-int/emit/hostname build/cov-int/emit/$(hostname)
+ - name: Lookup Coverity Build Tool hash
+ id: coverity-cache-lookup
+ shell: bash
+ run: |
+ hash=$(curl https://scan.coverity.com/download/cxx/linux64 --data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=hidapi&md5=1")
+ echo "coverity_hash=${hash}" >> $GITHUB_OUTPUT
+ - name: Get cached Coverity Build Tool
+ id: cov-build-cache
+ uses: actions/cache@v3
+ with:
+ path: cov-root
+ key: cov-root-cxx-linux64-${{ steps.coverity-cache-lookup.outputs.coverity_hash }}
+ - name: Get and configure Coverity
+ if: steps.cov-build-cache.outputs.cache-hit != 'true'
+ run: |
+ curl https://scan.coverity.com/download/cxx/linux64 --output coverity.tar.gz --data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=hidapi"
+ rm -rf cov-root
+ mkdir cov-root
+ tar -xzf coverity.tar.gz --strip 1 -C cov-root
+ ./cov-root/bin/cov-configure --gcc
+ - name: Make Coverity available in PATH
+ run: echo "$(pwd)/cov-root/bin" >> $GITHUB_PATH
+ - name: Build with Coverity
+ working-directory: build
+ run: |
+ cov-build --dir cov-int --append-log ninja
+ - name: Submit results to Coverity Scan
+ working-directory: build
+ run: |
+ tar -czf cov-int.tar.gz cov-int
+ curl --form token=${{ secrets.COVERITY_SCAN_TOKEN }} \
+ --form email=${{ secrets.COVERITY_SCAN_EMAIL }} \
+ --form file=@cov-int.tar.gz \
+ --form version="$GITHUB_SHA" \
+ --form description="Automatic HIDAPI build" \
+ https://scan.coverity.com/builds?project=hidapi
+ mv cov-int/emit/$(hostname) cov-int/emit/hostname
+ - name: Backup Coverity logs
+ uses: actions/upload-artifact@v3
+ with:
+ name: coverity-logs-windows-macos-linux
+ path: build/cov-int
+ retention-days: 7
diff --git a/src/hidapi/.github/workflows/docs.yaml b/src/hidapi/.github/workflows/docs.yaml
new file mode 100644
index 0000000..4ec09a5
--- /dev/null
+++ b/src/hidapi/.github/workflows/docs.yaml
@@ -0,0 +1,58 @@
+name: Docs
+
+on:
+ push:
+ branches: [master]
+ pull_request:
+ branches: [master]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+
+ - name: Install Doxygen static libclang deps
+ run: sudo apt-get install libclang1-12 libclang-cpp12
+
+ - name: Install Doxygen from SF binary archives
+ env:
+ DOXYGEN_VERSION: '1.9.6'
+ run: |
+ mkdir .doxygen && cd .doxygen
+ curl -L https://sourceforge.net/projects/doxygen/files/rel-$DOXYGEN_VERSION/doxygen-$DOXYGEN_VERSION.linux.bin.tar.gz > doxygen.tar.gz
+ gunzip doxygen.tar.gz
+ tar xf doxygen.tar
+ cd doxygen-$DOXYGEN_VERSION
+ sudo make install
+
+ - uses: actions/checkout@v3
+
+ - run: doxygen
+ working-directory: doxygen
+
+ - name: Save doxygen docs as artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: HIDAPI_doxygen_docs
+ path: ${{ github.workspace }}/doxygen/html
+
+ deploy-docs:
+ runs-on: ubuntu-latest
+ needs: [build]
+ if: github.ref_type == 'branch' && github.ref_name == 'master'
+ concurrency:
+ group: "github-pages-deploy"
+ cancel-in-progress: true
+ steps:
+ - name: downlod artifact
+ uses: actions/download-artifact@v3
+ with:
+ name: HIDAPI_doxygen_docs
+ path: docs
+
+ - name: upload to github pages
+ uses: peaceiris/actions-gh-pages@v3
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ publish_dir: ./docs
+ force_orphan: true
diff --git a/src/hidapi/.gitignore b/src/hidapi/.gitignore
new file mode 100644
index 0000000..048900e2
--- /dev/null
+++ b/src/hidapi/.gitignore
@@ -0,0 +1,32 @@
+
+# Autotools-added generated files
+Makefile.in
+aclocal.m4
+ar-lib
+autom4te.cache/
+config.*
+configure
+configure~
+compile
+depcomp
+install-sh
+libusb/Makefile.in
+linux/Makefile.in
+ltmain.sh
+mac/Makefile.in
+missing
+testgui/Makefile.in
+windows/Makefile.in
+
+Makefile
+stamp-h1
+libtool
+
+# macOS
+.DS_Store
+
+# Qt Creator
+CMakeLists.txt.user
+
+# doxgen output
+doxygen/html/
diff --git a/src/hidapi/AUTHORS.txt b/src/hidapi/AUTHORS.txt
index f311978..7193d71 100644
--- a/src/hidapi/AUTHORS.txt
+++ b/src/hidapi/AUTHORS.txt
@@ -10,7 +10,9 @@
Bug fixes
Correctness fixes
+libusb/hidapi Team:
+ Development/maintainance since June 4th 2019
For a comprehensive list of contributions, see the commit list at github:
- https://github.com/libusb/hidapi/commits/master
+ https://github.com/libusb/hidapi/graphs/contributors
diff --git a/src/hidapi/BUILD.autotools.md b/src/hidapi/BUILD.autotools.md
new file mode 100644
index 0000000..24b20a5
--- /dev/null
+++ b/src/hidapi/BUILD.autotools.md
@@ -0,0 +1,114 @@
+# Building HIDAPI using Autotools (deprecated)
+
+---
+**NOTE**: for all intentions and purposes the Autotools build scripts for HIDAPI are _deprecated_ and going to be obsolete in the future.
+HIDAPI Team recommends using CMake build for HIDAPI.
+If you are already using Autotools build scripts provided by HIDAPI,
+consider switching to CMake build scripts as soon as possible.
+
+---
+
+To be able to use Autotools to build HIDAPI, it has to be [installed](#installing-autotools)/available in the system.
+
+Make sure you've checked [prerequisites](BUILD.md#prerequisites) and installed all required dependencies.
+
+## Installing Autotools
+
+HIDAPI uses few specific tools/packages from Autotools: `autoconf`, `automake`, `libtool`.
+
+On different platforms or package managers, those could be named a bit differently or packaged together.
+You'll have to check the documentation/package list for your specific package manager.
+
+### Linux
+
+On Ubuntu the tools are available via APT:
+
+```sh
+sudo apt install autoconf automake libtool
+```
+
+### FreeBSD
+
+FreeBSD Autotools can be installed as:
+
+```sh
+pkg_add -r autotools
+```
+
+Additionally, on FreeBSD you will need to install GNU make:
+```sh
+pkg_add -r gmake
+```
+
+## Building HIDAPI with Autotools
+
+A simple command list, to build HIDAPI with Autotools as a _shared library_ and install in into your system:
+
+```sh
+./bootstrap # this prepares the configure script
+./configure
+make # build the library
+make install # as root, or using sudo, this will install hidapi into your system
+```
+
+`./configure` can take several arguments which control the build. A few commonly used options:
+```sh
+ --enable-testgui
+ # Enable the build of Foxit-based Test GUI. This requires Fox toolkit to
+ # be installed/available. See README.md#test-gui for remarks.
+
+ --prefix=/usr
+ # Specify where you want the output headers and libraries to
+ # be installed. The example above will put the headers in
+ # /usr/include and the binaries in /usr/lib. The default is to
+ # install into /usr/local which is fine on most systems.
+
+ --disable-shared
+ # By default, both shared and static libraries are going to be built/installed.
+ # This option disables shared library build, if only static library is required.
+```
+
+
+## Cross Compiling
+
+This section talks about cross compiling HIDAPI for Linux using Autotools.
+This is useful for using HIDAPI on embedded Linux targets. These
+instructions assume the most raw kind of embedded Linux build, where all
+prerequisites will need to be built first. This process will of course vary
+based on your embedded Linux build system if you are using one, such as
+OpenEmbedded or Buildroot.
+
+For the purpose of this section, it will be assumed that the following
+environment variables are exported.
+```sh
+$ export STAGING=$HOME/out
+$ export HOST=arm-linux
+```
+
+`STAGING` and `HOST` can be modified to suit your setup.
+
+### Prerequisites
+
+Depending on what backend you want to cross-compile, you also need to prepare the dependencies:
+`libusb` for libusb HIDAPI backend, or `libudev` for hidraw HIDAPI backend.
+
+An example of cross-compiling `libusb`. From `libusb` source directory, run:
+```sh
+./configure --host=$HOST --prefix=$STAGING
+make
+make install
+```
+
+An example of cross-comping `libudev` is not covered by this section.
+Check `libudev`'s documentation for details.
+
+### Building HIDAPI
+
+Build HIDAPI:
+```sh
+PKG_CONFIG_DIR= \
+PKG_CONFIG_LIBDIR=$STAGING/lib/pkgconfig:$STAGING/share/pkgconfig \
+PKG_CONFIG_SYSROOT_DIR=$STAGING \
+./configure --host=$HOST --prefix=$STAGING
+# make / make install - same as for a regular build
+```
diff --git a/src/hidapi/BUILD.cmake.md b/src/hidapi/BUILD.cmake.md
new file mode 100644
index 0000000..9c51d0c
--- /dev/null
+++ b/src/hidapi/BUILD.cmake.md
@@ -0,0 +1,280 @@
+# Building HIDAPI using CMake
+
+To build HIDAPI with CMake, it has to be [installed](#installing-cmake)/available in the system.
+
+Make sure you've checked [prerequisites](BUILD.md#prerequisites) and installed all required dependencies.
+
+HIDAPI CMake build system allows you to build HIDAPI in two generally different ways:
+1) As a [standalone package/library](#standalone-package-build);
+2) As [part of a larger CMake project](#hidapi-as-a-subdirectory).
+
+**TL;DR**: if you're experienced developer and have been working with CMake projects or have been written some of your own -
+most of this document may not be of interest for you; just check variables names, its default values and the target names.
+
+## Installing CMake
+
+CMake can be installed either using your system's package manager,
+or by downloading an installer/prebuilt version from the [official website](https://cmake.org/download/).
+
+On most \*nix systems, the prefered way to install CMake is via package manager,
+e.g. `sudo apt install cmake`.
+
+On Windows CMake could be provided by your development environment (e.g. by Visual Studio Installer or MinGW installer),
+or you may install it system-wise using the installer from the official website.
+
+On macOS CMake may be installed by Homebrew/MacPorts or using the installer from the official website.
+
+## Standalone package build
+
+To build HIDAPI as a standalone package, you follow [general steps](https://cmake.org/runningcmake/) of building any CMake project.
+
+An example of building HIDAPI with CMake:
+```sh
+# precondition: create a <build dir> somewhere on the filesystem (preferably outside of the HIDAPI source)
+# this is the place where all intermediate/build files are going to be located
+cd <build dir>
+# configure the build
+cmake <HIDAPI source dir>
+# build it!
+cmake --build .
+# install library; by default installs into /usr/local/
+cmake --build . --target install
+# NOTE: you need to run install command as root, to be able to install into /usr/local/
+```
+Such invocation will use the default (as per CMake magic) compiler/build environment available in your system.
+
+You may pass some additional CMake variables to control the build configuration as `-D<CMake Variable>=value`.
+E.g.:
+```sh
+# install command now would install things into /usr
+cmake <HIDAPI source dir> -DCMAKE_INSTALL_PREFIX=/usr
+```
+
+<details>
+ <summary>Using a specific CMake generator</summary>
+
+An example of using `Ninja` as a CMake generator:
+
+```sh
+cd <build dir>
+# configure the build
+cmake -GNinja <HIDAPI source dir>
+# we know, that CMake has generated build files for Ninja,
+# so we can use `ninja` directly, instead of `cmake --build .`
+ninja
+# install library
+ninja install
+```
+
+`-G` here specifies a native build system CMake would generate build files for.
+Check [CMake Documentation](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) for a list of available generators (system-specific).
+
+</details><br>
+
+Some of the [standard](https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html) CMake variables you may want to use to configure a build:
+
+- [`CMAKE_INSTALL_PREFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html) - prefix where `install` target would install the library(ies);
+- [`CMAKE_BUILD_TYPE`](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) - standard possible values: `Debug`, `Release`, `RelWithDebInfo`, `MinSizeRel`; Defaults to `Release` for HIDAPI, if not specified;
+- [`BUILD_SHARED_LIBS`](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html) - when set to TRUE, HIDAPI is built as a shared library, otherwise build statically; Defaults to `TRUE` for HIDAPI, if not specified;
+
+<details>
+ <summary>macOS-specific variables</summary>
+
+ - [`CMAKE_FRAMEWORK`](https://cmake.org/cmake/help/latest/variable/CMAKE_FRAMEWORK.html) - (since CMake 3.15) when set to TRUE, HIDAPI is built as a framework library, otherwise build as a regular static/shared library; Defaults to `FALSE` for HIDAPI, if not specified;
+ - [`CMAKE_OSX_DEPLOYMENT_TARGET`](https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_DEPLOYMENT_TARGET.html) - minimum version of the target platform (e.g. macOS or iOS) on which the target binaries are to be deployed; defaults to a maximum supported target platform by currently used XCode/Toolchain;
+
+</details><br>
+
+HIDAPI-specific CMake variables:
+
+- `HIDAPI_BUILD_HIDTEST` - when set to TRUE, build a small test application `hidtest`;
+- `HIDAPI_WITH_TESTS` - when set to TRUE, build all (unit-)tests;
+currently this option is only available on Windows, since only Windows backend has tests;
+
+<details>
+ <summary>Linux-specific variables</summary>
+
+ - `HIDAPI_WITH_HIDRAW` - when set to TRUE, build HIDRAW-based implementation of HIDAPI (`hidapi-hidraw`), otherwise don't build it; defaults to TRUE;
+ - `HIDAPI_WITH_LIBUSB` - when set to TRUE, build LIBUSB-based implementation of HIDAPI (`hidapi-libusb`), otherwise don't build it; defaults to TRUE;
+
+ **NOTE**: at least one of `HIDAPI_WITH_HIDRAW` or `HIDAPI_WITH_LIBUSB` has to be set to TRUE.
+
+</details><br>
+
+To see all most-useful CMake variables available for HIDAPI, one of the most convenient ways is too use [`cmake-gui`](https://cmake.org/cmake/help/latest/manual/cmake-gui.1.html) tool ([example](https://cmake.org/runningcmake/)).
+
+_NOTE_: HIDAPI packages built by CMake can be used with `pkg-config`, as if built with [Autotools](BUILD.autotools.md).
+
+### MSVC and Ninja
+It is possible to build a CMake project (including HIDAPI) using MSVC compiler and Ninja (for medium and larger projects it is so much faster than msbuild).
+
+For that:
+1) Open cmd.exe;
+2) Setup MSVC build environment variables, e.g.: `vcvarsall.bat x64`, where:
+ - `vcvarsall.bat` is an environment setup script of your MSVC toolchain installation;<br>For MSVC 2019 Community edition it is located at: `C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\`;
+ - `x64` -a target architecture to build;
+3) Follow general build steps, and use `Ninja` as a generator.
+
+### Using HIDAPI in a CMake project
+
+When HIDAPI is used as a standalone package (either installed into the system or built manually and installed elsewhere), the simplest way to use it is as showed in the example:
+
+```cmake
+project(my_application)
+
+add_executable(my_application main.c)
+
+find_package(hidapi REQUIRED)
+target_link_libraries(my_application PRIVATE hidapi::hidapi)
+```
+
+If HIDAPI isn't installed in your system, or `find_package` cannot find HIDAPI by default for any other reasons,
+the recommended way manually specify which HIDAPI package to use is via `hidapi_ROOT` CMake variable, e.g.:
+`-Dhidapi_ROOT=<path to HIDAPI installation prefix>`.
+
+_NOTE_: usage of `hidapi_ROOT` is only possible (and recommended) with CMake 3.12 and higher. For older versions of CMake you'd need to specify [`CMAKE_PREFIX_PATH`](https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html#variable:CMAKE_PREFIX_PATH) instead.
+
+Check with [`find_package`](https://cmake.org/cmake/help/latest/command/find_package.html) documentation if you need more details.
+
+Available CMake targets after successful `find_package(hidapi)`:
+- `hidapi::hidapi` - indented to be used in most cases;
+- `hidapi::include` - if you need only to include `<hidapi.h>` but not link against the library;
+- `hidapi::winapi` - same as `hidapi::hidapi` on Windows; available only on Windows;
+- `hidapi::darwin` - same as `hidapi::hidapi` on macOS; available only on macOS;
+- `hidapi::libusb` - available when libusb backend is used/available;
+- `hidapi::hidraw` - available when hidraw backend is used/available on Linux;
+
+**NOTE**: on Linux often both `hidapi::libusb` and `hidapi::hidraw` backends are available; in that case `hidapi::hidapi` is an alias for **`hidapi::hidraw`**. The motivation is that `hidraw` backend is a native Linux kernel implementation of HID protocol, and supports various HID devices (USB, Bluetooth, I2C, etc.). If `hidraw` backend isn't built at all (`hidapi::libusb` is the only target) - `hidapi::hidapi` is an alias for `hidapi::libusb`.
+If you're developing a cross-platform application and you are sure you need to use `libusb` backend on Linux, the simple way to achieve this is:
+```cmake
+if(TARGET hidapi::libusb)
+ target_link_libraries(my_project PRIVATE hidapi::libusb)
+else()
+ target_link_libraries(my_project PRIVATE hidapi::hidapi)
+endif()
+```
+
+## HIDAPI as a subdirectory
+
+HIDAPI can be easily used as a subdirectory of a larger CMake project:
+```cmake
+# root CMakeLists.txt
+cmake_minimum_required(VERSION 3.4.3 FATAL_ERROR)
+
+add_subdirectory(hidapi)
+add_subdirectory(my_application)
+
+# my_application/CMakeLists.txt
+project(my_application)
+
+add_executable(my_application main.c)
+
+# NOTE: no `find_package` is required, since HIDAPI targets are already a part of the project tree
+target_link_libraries(my_application PRIVATE hidapi::hidapi)
+```
+Lets call this "larger project" a "host project".
+
+All of the variables described in [standalone build](#standalone-package-build) section can be used to control HIDAPI build in case of a subdirectory, e.g.:
+```cmake
+set(HIDAPI_WITH_LIBUSB FALSE) # surely will be used only on Linux
+set(BUILD_SHARED_LIBS FALSE) # HIDAPI as static library on all platforms
+add_subdirectory(hidapi)
+```
+
+<details>
+ <summary>NOTE</summary>
+
+ If you project happen to use `BUILD_SHARED_LIBS` as a `CACHE` variable globally for you project, setting it as simple variable, as showed above _will have not affect_ up until _CMake 3.13_. See [CMP0077](https://cmake.org/cmake/help/latest/policy/CMP0077.html) for details.
+</details><br>
+
+There are several important differences in the behavior of HIDAPI CMake build system when CMake is built as standalone package vs subdirectory build:
+
+1) In _standalone build_ a number of standard and HIDAPI-specific variables are marked as _cache variables_ or _options_.
+This is done for convenience: when you're building HIDAPI as a standalone package and using tools like `cmake-gui` - those are highlighted as variables that can be changed and has some short description/documentation. E.g.:
+<br>
+E.g.2:<br>
+<br>
+When HIDAPI is built as a _subdirectory_ - **_none of the variables are marked for cache or as options_** by HIDAPI.
+This is done to let the host project's developer decide what is important (what needs to be highlighted) and what's not.
+
+2) The default behavior/default value for some of the variables is a bit different:
+ - by default, none of HIDAPI targets are [installed](https://cmake.org/cmake/help/latest/command/install.html); if required, HIDAPI targets can be installed by host project _after_ including HIDAPI subdirectory (requires CMake 3.13 or later); **or**, the default installation can be enabled by setting `HIDAPI_INSTALL_TARGETS` variable _before_ including HIDAPI subdirectory.
+ HIDAPI uses [GNUInstallDirs](https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html) to specify install locations. Variables like `CMAKE_INSTALL_LIBDIR` can be used to control HIDAPI's installation locations. E.g.:
+ ```cmake
+ # enable the installation if you need it
+ set(HIDAPI_INSTALL_TARGETS ON)
+ # (optionally) change default installation locations if it makes sense for your target platform, etc.
+ set(CMAKE_INSTALL_LIBDIR "lib64")
+ add_subdirectory(hidapi)
+ ```
+ - HIDAPI prints its version during the configuration when built as a standalone package; to enable this for subdirectory builds - set `HIDAPI_PRINT_VERSION` to TRUE before including HIDAPI;
+
+3) In a subdirectory build, HIDAPI _doesn't modify or set any of the CMake variables_ that may change the build behavior.
+ For instance, in a _standalone build_, if CMAKE_BUILD_TYPE or BUILD_SHARED_LIBS variables are not set, those are defaulted to "Release" and "TRUE" explicitly.
+ In a _subdirectory build_, even if not set, those variables remain unchanged, so a host project's developer has a full control over the HIDAPI build configuration.
+
+Available CMake targets after `add_subdirectory(hidapi)` _are the same as in case of [standalone build](#standalone-package-build)_, and a few additional ones:
+- `hidapi_include` - the interface library; `hidapi::hidapi` is an alias of it;
+- `hidapi_winapi` - library target on Windows; `hidapi::winapi` is an alias of it;
+- `hidapi_darwin` - library target on macOS; `hidapi::darwin` is an alias of it;
+- `hidapi_libusb` - library target for libusb backend; `hidapi::libusb` is an alias of it;
+- `hidapi_hidraw` - library target for hidraw backend; `hidapi::hidraw` is an alias of it;
+- `hidapi-libusb` - an alias of `hidapi_libusb` for compatibility with raw library name;
+- `hidapi-hidraw` - an alias of `hidapi_hidraw` for compatibility with raw library name;
+- `hidapi` - an alias of `hidapi_winapi` or `hidapi_darwin` on Windows or macOS respectfully.
+
+Advanced:
+- Why would I need additional targets described in this section above, if I already have alias targets compatible with `find_package`?
+ - an example:
+ ```cmake
+ add_subdirectory(hidapi)
+ if(TARGET hidapi_libusb)
+ # see libusb/hid.c for usage of `NO_ICONV`
+ target_compile_definitions(hidapi_libusb PRIVATE NO_ICONV)
+ endif()
+ ```
+
+## Both Shared and Static build
+
+If you're a former (or present) user of Autotools build scripts for HIDAPI, or you're a package manager maintainer and you're often working with those - you're likely asking how to build HIDAPI with CMake and get both Shared and Static libraries (as would be done by Autotools: `./configure --enable-static --enable-shared ...`).
+
+CMake doesn't have such option of-the-box and it is decided not to introduce any manual CMake-level workarounds for HIDAPI on this matter.
+
+If you want to mimic the Autotools behavior, it is possible by building/installing first the static version of the library and then shared version of the library. The installation folder (`CMAKE_INSTALL_PREFIX`) should point to the same directory for both variants, that way:
+- both static and shared library binaries will be available and usable;
+- a single header file(s) for both of them;
+- Autotools/pkg-config (`.pc`) files will be generated and usable _as if_ generated by Autotools natively and build configured with both `-enable-static --enable-shared` options;
+- CMake package scripts will be generated and fully usable, but _only the last build installed_, i.e. if the last was installed Shared version of the binary - CMake targets found by `find_package(hidapi)` would point to a Shared binaries.
+
+There is a historical discussion, why such solution is simplest/preferable: https://github.com/libusb/hidapi/issues/424
+
+#### TL;DR/Sample
+
+```sh
+# First - configure/build
+
+# Static libraries
+cmake -S <HIDAPI source dir> -B "<build dir>/static" -DCMAKE_INSTALL_PREFIX=<your installation prefix> -DBUILD_SHARED_LIBS=FALSE
+cmake --build "<build dir>/static"
+# Shared libraries
+cmake -S <HIDAPI source dir> -B "<build dir>/shared" -DCMAKE_INSTALL_PREFIX=<your installation prefix> -DBUILD_SHARED_LIBS=TRUE
+cmake --build "<build dir>/shared"
+
+# (Optionally) change the installation destination.
+# NOTE1: this is supported by CMake only on UNIX platforms
+# See https://cmake.org/cmake/help/latest/envvar/DESTDIR.html
+# NOTE2: this is not the same as `CMAKE_INSTALL_PREFIX` set above
+# NOTE3: this is only required if you have a staging dir other than the final runtime dir,
+# e.g. during cross-compilation
+export DESTDIR="$STAGING_DIR"
+
+#
+# Install the libraries
+# NOTE: order of installation matters - install Shared variant *the last*
+
+# Static libraries
+cmake --install "<build dir>/static"
+# Shared libraries
+cmake --install "<build dir>/shared"
+
+```
diff --git a/src/hidapi/BUILD.md b/src/hidapi/BUILD.md
new file mode 100644
index 0000000..d7a3546
--- /dev/null
+++ b/src/hidapi/BUILD.md
@@ -0,0 +1,127 @@
+# Building HIDAPI from Source
+
+## Table of content
+
+* [Intro](#intro)
+* [Prerequisites](#prerequisites)
+ * [Linux](#linux)
+ * [FreeBSD](#freebsd)
+ * [Mac](#mac)
+ * [Windows](#windows)
+* [Embedding HIDAPI directly into your source tree](#embedding-hidapi-directly-into-your-source-tree)
+* [Building the manual way on Unix platforms](#building-the-manual-way-on-unix-platforms)
+* [Building on Windows](#building-on-windows)
+
+## Intro
+
+For various reasons, you may need to build HIDAPI on your own.
+
+It can be done in several different ways:
+- using [CMake](BUILD.cmake.md);
+- using [Autotools](BUILD.autotools.md) (deprecated);
+- using [manual makefiles](#building-the-manual-way-on-unix-platforms);
+- using `Meson` (requires CMake);
+
+**Autotools** build system is historically the first mature build system for
+HIDAPI. The most common usage of it is in its separate README: [BUILD.autotools.md](BUILD.autotools.md).<br/>
+NOTE: for all intentions and purposes the Autotools build scripts for HIDAPI are _deprecated_ and going to be obsolete in the future.
+HIDAPI Team recommends using CMake build for HIDAPI.
+
+**CMake** build system is de facto an industry standard for many open-source and proprietary projects and solutions.
+HIDAPI is one of the projects which use the power of CMake to its advantage.
+More documentation is available in its separate README: [BUILD.cmake.md](BUILD.cmake.md).
+
+**Meson** build system for HIDAPI is designed as a [wrapper](https://mesonbuild.com/CMake-module.html) over CMake build script.
+It is present for the convenience of Meson users who need to use HIDAPI and need to be sure HIDAPI is built in accordance with officially supported build scripts.<br>
+In the Meson script of your project you need a `hidapi = subproject('hidapi')` subproject, and `hidapi.get_variable('hidapi_dep')` as your dependency.
+There are also backend/platform-specific dependencies available: `hidapi_winapi`, `hidapi_darwin`, `hidapi_hidraw`, `hidapi_libusb`.
+
+If you don't know where to start to build HIDAPI, we recommend starting with [CMake](BUILD.cmake.md) build.
+
+## Prerequisites:
+
+Regardless of what build system you choose to use, there are specific dependencies for each platform/backend.
+
+### Linux:
+
+Depending on which backend you're going to build, you'll need to install
+additional development packages. For `linux/hidraw` backend, you need a
+development package for `libudev`. For `libusb` backend, naturally, you need
+`libusb` development package.
+
+On Debian/Ubuntu systems these can be installed by running:
+```sh
+# required only by hidraw backend
+sudo apt install libudev-dev
+# required only by libusb backend
+sudo apt install libusb-1.0-0-dev
+```
+
+### FreeBSD:
+
+On FreeBSD, you will need to install libiconv. This is done by running
+the following:
+```sh
+pkg_add -r libiconv
+```
+
+### Mac:
+
+Make sure you have XCode installed and its Command Line Tools.
+
+### Windows:
+
+You just need a compiler. You may use Visual Studio or Cygwin/MinGW,
+depending on which environment is best for your needs.
+
+## Embedding HIDAPI directly into your source tree
+
+Instead of using one of the provided standalone build systems,
+you may want to integrate HIDAPI directly into your source tree.
+
+---
+If your project uses CMake as a build system, it is safe to add HIDAPI as a [subdirectory](BUILD.cmake.md#hidapi-as-a-subdirectory).
+
+---
+If _the only option_ that works for you is adding HIDAPI sources directly
+to your project's build system, then you need:
+- include a _single source file_ into your project's build system,
+depending on your platform and the backend you want to use:
+ - [`windows\hid.c`](windows/hid.c);
+ - [`linux/hid.c`](linux/hid.c);
+ - [`libusb/hid.c`](libusb/hid.c);
+ - [`mac/hid.c`](mac/hid.c);
+- add a [`hidapi`](hidapi) folder to the include path when building `hid.c`;
+- make the platform/backend specific [dependencies](#prerequisites) available during the compilation/linking, when building `hid.c`;
+
+NOTE: the above doesn't guarantee that having a copy of `<backend>/hid.c` and `hidapi/hidapi.h` is enough to build HIDAPI.
+The only guarantee that `<backend>/hid.c` includes all necessary sources to compile it as a single file.
+
+Check the manual makefiles for a simple example/reference of what are the dependencies of each specific backend.
+
+## Building the manual way on Unix platforms
+
+Manual Makefiles are provided mostly to give the user an idea what it takes
+to build a program which embeds HIDAPI directly inside of it. These should
+really be used as examples only. If you want to build a system-wide shared
+library, use one of the build systems mentioned above.
+
+To build HIDAPI using the manual Makefiles, change the directory
+of your platform and run make. For example, on Linux run:
+```sh
+cd linux/
+make -f Makefile-manual
+```
+
+## Building on Windows
+
+To build the HIDAPI DLL on Windows using Visual Studio, build the `.sln` file
+in the `windows/` directory.
+
+To build HIDAPI using MinGW or Cygwin using Autotools, use general Autotools
+ [instruction](BUILD.autotools.md).
+
+Any windows builds (MSVC or MinGW/Cygwin) are also supported by [CMake](BUILD.cmake.md).
+
+If you are looking for information regarding DDK build of HIDAPI:
+- the build has been broken for a while and now the support files are obsolete.
diff --git a/src/hidapi/CMakeLists.txt b/src/hidapi/CMakeLists.txt
new file mode 100644
index 0000000..e18ee23
--- /dev/null
+++ b/src/hidapi/CMakeLists.txt
@@ -0,0 +1,105 @@
+cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR)
+
+if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
+ add_subdirectory(src)
+ # compatibility with find_package() vs add_subdirectory
+ set(hidapi_VERSION "${hidapi_VERSION}" PARENT_SCOPE)
+ return()
+endif()
+# All of the below in this file is meant for a standalone build.
+# When building as a subdirectory of a larger project, most of the options may not make sense for it,
+# so it is up to developer to configure those, e.g.:
+#
+# # a subfolder of a master project, e.g.: 3rdparty/hidapi/CMakeLists.txt
+#
+# set(HIDAPI_WITH_HIDRAW OFF)
+# set(CMAKE_FRAMEWORK ON)
+# # and keep everything else to their defaults
+# add_subdirectory(hidapi)
+#
+
+set(DEFAULT_CMAKE_BUILD_TYPES "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+if(NOT DEFINED CMAKE_BUILD_TYPE OR NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE "Release" CACHE STRING "${DEFAULT_CMAKE_BUILD_TYPES}" FORCE)
+endif()
+# This part is for convenience, when used one of the standard build types with cmake-gui
+list(FIND DEFAULT_CMAKE_BUILD_TYPES "${CMAKE_BUILD_TYPE}" _build_type_index)
+if(${_build_type_index} GREATER -1)
+ # set it optionally, so a custom CMAKE_BUILD_TYPE can be used as well, if needed
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${DEFAULT_CMAKE_BUILD_TYPES})
+endif()
+unset(_build_type_index)
+#
+
+project(hidapi LANGUAGES C)
+
+if(APPLE)
+ if(NOT CMAKE_VERSION VERSION_LESS "3.15")
+ option(CMAKE_FRAMEWORK "Build macOS/iOS Framework version of the library" OFF)
+ endif()
+elseif(NOT WIN32)
+ if(CMAKE_SYSTEM_NAME MATCHES "Linux")
+ option(HIDAPI_WITH_HIDRAW "Build HIDRAW-based implementation of HIDAPI" ON)
+ option(HIDAPI_WITH_LIBUSB "Build LIBUSB-based implementation of HIDAPI" ON)
+ endif()
+endif()
+
+option(BUILD_SHARED_LIBS "Build shared version of the libraries, otherwise build statically" ON)
+
+set(HIDAPI_INSTALL_TARGETS ON)
+set(HIDAPI_PRINT_VERSION ON)
+
+set(IS_DEBUG_BUILD OFF)
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(IS_DEBUG_BUILD ON)
+endif()
+
+option(HIDAPI_ENABLE_ASAN "Build HIDAPI with ASAN address sanitizer instrumentation" OFF)
+
+if(HIDAPI_ENABLE_ASAN)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
+ if(MSVC)
+ # the default is to have "/INCREMENTAL" which causes a warning when "-fsanitize=address" is present
+ set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /INCREMENTAL:NO")
+ set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /INCREMENTAL:NO")
+ set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /INCREMENTAL:NO")
+ set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /INCREMENTAL:NO")
+ endif()
+endif()
+
+if(WIN32)
+ # so far only Windows has tests
+ option(HIDAPI_WITH_TESTS "Build HIDAPI (unit-)tests" ${IS_DEBUG_BUILD})
+else()
+ set(HIDAPI_WITH_TESTS OFF)
+endif()
+
+if(HIDAPI_WITH_TESTS)
+ enable_testing()
+endif()
+
+if(WIN32)
+ option(HIDAPI_BUILD_PP_DATA_DUMP "Build small Windows console application pp_data_dump.exe" ${IS_DEBUG_BUILD})
+endif()
+
+add_subdirectory(src)
+
+option(HIDAPI_BUILD_HIDTEST "Build small console test application hidtest" ${IS_DEBUG_BUILD})
+if(HIDAPI_BUILD_HIDTEST)
+ add_subdirectory(hidtest)
+endif()
+
+if(HIDAPI_ENABLE_ASAN)
+ if(NOT MSVC)
+ # MSVC doesn't recognize those options, other compilers - requiring it
+ foreach(HIDAPI_TARGET hidapi_winapi hidapi_darwin hidapi_hidraw hidapi_libusb hidtest_hidraw hidtest_libusb hidtest)
+ if(TARGET ${HIDAPI_TARGET})
+ if(BUILD_SHARED_LIBS)
+ target_link_options(${HIDAPI_TARGET} PRIVATE -fsanitize=address)
+ else()
+ target_link_options(${HIDAPI_TARGET} PUBLIC -fsanitize=address)
+ endif()
+ endif()
+ endforeach()
+ endif()
+endif()
diff --git a/src/hidapi/HACKING.txt b/src/hidapi/HACKING.txt
index 761d4b6..e06b533 100644
--- a/src/hidapi/HACKING.txt
+++ b/src/hidapi/HACKING.txt
@@ -1,15 +1,19 @@
This file is mostly for the maintainer.
-1. Build hidapi.dll
-2. Build hidtest.exe in DEBUG and RELEASE
-3. Commit all
+Updating a Version:
+1. Update VERSION file.
+2. HID_API_VERSION_MAJOR/HID_API_VERSION_MINOR/HID_API_VERSION_PATCH in hidapi.h.
-4. Run the Following
- export VERSION=0.1.0
- export TAG_NAME=hidapi-$VERSION
- git tag $TAG_NAME
- git archive --format zip --prefix $TAG_NAME/ $TAG_NAME >../$TAG_NAME.zip
-5. Test the zip file.
-6. Run the following:
- git push origin $TAG_NAME
+Before firing a new release:
+1. Run the "Checks" Githtub Action
+2. Make sure no defects are found at: https://scan.coverity.com/projects/hidapi
+3. Fix if any
+Firing a new release:
+1. Update the Version (if not yet updated).
+2. Prepare the Release Notes.
+3. Store the Release Notes into a file.
+4. Create locally an annotated git tag with release notes attached, e.g.: `git tag -aF ../hidapi_release_notes hidapi-<VERSION>`
+5. Push newly created tag: `git push origin hidapi-<VERSION>`
+6. Grab the hidapi-win.zip from Summary page of "GitHub Builds" Action for latest master build.
+7. Create a Github Release with hidapi-win.zip attached, for newly created tag.
diff --git a/src/hidapi/Makefile.am b/src/hidapi/Makefile.am
index 3382a1f..00bcb73 100644
--- a/src/hidapi/Makefile.am
+++ b/src/hidapi/Makefile.am
@@ -23,10 +23,6 @@
SUBDIRS += mac
endif
-if OS_IOS
-SUBDIRS += ios
-endif
-
if OS_FREEBSD
SUBDIRS += libusb
endif
@@ -35,6 +31,10 @@
SUBDIRS += libusb
endif
+if OS_HAIKU
+SUBDIRS += libusb
+endif
+
if OS_WINDOWS
SUBDIRS += windows
endif
@@ -48,7 +48,7 @@
EXTRA_DIST = udev doxygen
dist_doc_DATA = \
- README.txt \
+ README.md \
AUTHORS.txt \
LICENSE-bsd.txt \
LICENSE-gpl3.txt \
diff --git a/src/hidapi/README.md b/src/hidapi/README.md
new file mode 100644
index 0000000..257b9f3
--- /dev/null
+++ b/src/hidapi/README.md
@@ -0,0 +1,196 @@
+## HIDAPI library for Windows, Linux, FreeBSD and macOS
+
+| CI instance | Status |
+|----------------------|--------|
+| `Linux/macOS/Windows (master)` | [](https://github.com/libusb/hidapi/actions/workflows/builds.yml?query=branch%3Amaster) |
+| `Windows (master)` | [](https://ci.appveyor.com/project/libusb/hidapi/branch/master) |
+| `BSD, last build (branch/PR)` | [](https://builds.sr.ht/~z3ntu/hidapi) |
+| `Coverity Scan (last)` |  |
+
+HIDAPI is a multi-platform library which allows an application to interface
+with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and macOS.
+HIDAPI can be either built as a shared library (`.so`, `.dll` or `.dylib`) or
+can be embedded directly into a target application by adding a _single source_
+file (per platform) and a single header.<br>
+See [remarks](BUILD.md#embedding-hidapi-directly-into-your-source-tree) on embedding _directly_ into your build system.
+
+HIDAPI library was originally developed by Alan Ott ([signal11](https://github.com/signal11)).
+
+It was moved to [libusb/hidapi](https://github.com/libusb/hidapi) on June 4th, 2019, in order to merge important bugfixes and continue development of the library.
+
+## Table of Contents
+
+* [About](#about)
+ * [Test GUI](#test-gui)
+ * [Console Test App](#console-test-app)
+* [What Does the API Look Like?](#what-does-the-api-look-like)
+* [License](#license)
+* [Installing HIDAPI](#installing-hidapi)
+* [Build from Source](#build-from-source)
+
+
+## About
+
+### HIDAPI has four back-ends:
+* Windows (using `hid.dll`)
+* Linux/hidraw (using the Kernel's hidraw driver)
+* libusb (using libusb-1.0 - Linux/BSD/other UNIX-like systems)
+* macOS (using IOHidManager)
+
+On Linux, either the hidraw or the libusb back-end can be used. There are
+tradeoffs, and the functionality supported is slightly different. Both are
+built by default. It is up to the application linking to hidapi to choose
+the backend at link time by linking to either `libhidapi-libusb` or
+`libhidapi-hidraw`.
+
+Note that you will need to install an udev rule file with your application
+for unprivileged users to be able to access HID devices with hidapi. Refer
+to the [69-hid.rules](udev/69-hid.rules) file in the `udev` directory
+for an example.
+
+#### __Linux/hidraw__ (`linux/hid.c`):
+
+This back-end uses the hidraw interface in the Linux kernel, and supports
+both USB and Bluetooth HID devices. It requires kernel version at least 2.6.39
+to build. In addition, it will only communicate with devices which have hidraw
+nodes associated with them.
+Keyboards, mice, and some other devices which are blacklisted from having
+hidraw nodes will not work. Fortunately, for nearly all the uses of hidraw,
+this is not a problem.
+
+#### __Linux/FreeBSD/libusb__ (`libusb/hid.c`):
+
+This back-end uses libusb-1.0 to communicate directly to a USB device. This
+back-end will of course not work with Bluetooth devices.
+
+### Test GUI
+
+HIDAPI also comes with a Test GUI. The Test GUI is cross-platform and uses
+Fox Toolkit <http://www.fox-toolkit.org>. It will build on every platform
+which HIDAPI supports. Since it relies on a 3rd party library, building it
+is optional but it is useful when debugging hardware.
+
+NOTE: Test GUI based on Fox Toolkit is not actively developed nor supported
+by HIDAPI team. It is kept as a historical artifact. It may even work sometime
+or on some platforms, but it is not going to get any new features or bugfixes.
+
+Instructions for installing Fox-Toolkit on each platform is not provided.
+Make sure to use Fox-Toolkit v1.6 if you choose to use it.
+
+### Console Test App
+
+If you want to play around with your HID device before starting
+any development with HIDAPI and using a GUI app is not an option for you, you may try [`hidapitester`](https://github.com/todbot/hidapitester).
+
+This app has a console interface for most of the features supported
+by HIDAPI library.
+
+## What Does the API Look Like?
+
+The API provides the most commonly used HID functions including sending
+and receiving of input, output, and feature reports. The sample program,
+which communicates with a heavily hacked up version of the Microchip USB
+Generic HID sample looks like this (with error checking removed for
+simplicity):
+
+**Warning: Only run the code you understand, and only when it conforms to the
+device spec. Writing data (`hid_write`) at random to your HID devices can break them.**
+
+```c
+#include <stdio.h> // printf
+#include <wchar.h> // wchar_t
+
+#include <hidapi.h>
+
+#define MAX_STR 255
+
+int main(int argc, char* argv[])
+{
+ int res;
+ unsigned char buf[65];
+ wchar_t wstr[MAX_STR];
+ hid_device *handle;
+ int i;
+
+ // Initialize the hidapi library
+ res = hid_init();
+
+ // Open the device using the VID, PID,
+ // and optionally the Serial number.
+ handle = hid_open(0x4d8, 0x3f, NULL);
+ if (!handle) {
+ printf("Unable to open device\n");
+ hid_exit();
+ return 1;
+ }
+
+ // Read the Manufacturer String
+ res = hid_get_manufacturer_string(handle, wstr, MAX_STR);
+ printf("Manufacturer String: %ls\n", wstr);
+
+ // Read the Product String
+ res = hid_get_product_string(handle, wstr, MAX_STR);
+ printf("Product String: %ls\n", wstr);
+
+ // Read the Serial Number String
+ res = hid_get_serial_number_string(handle, wstr, MAX_STR);
+ printf("Serial Number String: (%d) %ls\n", wstr[0], wstr);
+
+ // Read Indexed String 1
+ res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
+ printf("Indexed String 1: %ls\n", wstr);
+
+ // Toggle LED (cmd 0x80). The first byte is the report number (0x0).
+ buf[0] = 0x0;
+ buf[1] = 0x80;
+ res = hid_write(handle, buf, 65);
+
+ // Request state (cmd 0x81). The first byte is the report number (0x0).
+ buf[0] = 0x0;
+ buf[1] = 0x81;
+ res = hid_write(handle, buf, 65);
+
+ // Read requested state
+ res = hid_read(handle, buf, 65);
+
+ // Print out the returned buffer.
+ for (i = 0; i < 4; i++)
+ printf("buf[%d]: %d\n", i, buf[i]);
+
+ // Close the device
+ hid_close(handle);
+
+ // Finalize the hidapi library
+ res = hid_exit();
+
+ return 0;
+}
+```
+
+You can also use [hidtest/test.c](hidtest/test.c)
+as a starting point for your applications.
+
+
+## License
+
+HIDAPI may be used by one of three licenses as outlined in [LICENSE.txt](LICENSE.txt).
+
+## Installing HIDAPI
+
+If you want to build your own application that uses HID devices with HIDAPI,
+you need to get HIDAPI development package.
+
+Depending on what your development environment is, HIDAPI likely to be provided
+by your package manager.
+
+For instance on Ubuntu, HIDAPI is available via APT:
+```sh
+sudo apt install libhidapi-dev
+```
+
+HIDAPI package name for other systems/package managers may differ.
+Check the documentation/package list of your package manager.
+
+## Build from Source
+
+Check [BUILD.md](BUILD.md) for details.
diff --git a/src/hidapi/README.txt b/src/hidapi/README.txt
deleted file mode 100644
index 756901e..0000000
--- a/src/hidapi/README.txt
+++ /dev/null
@@ -1,339 +0,0 @@
- HIDAPI library for Windows, Linux, FreeBSD and Mac OS X
- =========================================================
-
-About
-======
-
-HIDAPI is a multi-platform library which allows an application to interface
-with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and Mac
-OS X. HIDAPI can be either built as a shared library (.so or .dll) or
-can be embedded directly into a target application by adding a single source
-file (per platform) and a single header.
-
-HIDAPI has four back-ends:
- * Windows (using hid.dll)
- * Linux/hidraw (using the Kernel's hidraw driver)
- * Linux/libusb (using libusb-1.0)
- * FreeBSD (using libusb-1.0)
- * Mac (using IOHidManager)
-
-On Linux, either the hidraw or the libusb back-end can be used. There are
-tradeoffs, and the functionality supported is slightly different.
-
-Linux/hidraw (linux/hid.c):
-This back-end uses the hidraw interface in the Linux kernel. While this
-back-end will support both USB and Bluetooth, it has some limitations on
-kernels prior to 2.6.39, including the inability to send or receive feature
-reports. In addition, it will only communicate with devices which have
-hidraw nodes associated with them. Keyboards, mice, and some other devices
-which are blacklisted from having hidraw nodes will not work. Fortunately,
-for nearly all the uses of hidraw, this is not a problem.
-
-Linux/FreeBSD/libusb (libusb/hid.c):
-This back-end uses libusb-1.0 to communicate directly to a USB device. This
-back-end will of course not work with Bluetooth devices.
-
-HIDAPI also comes with a Test GUI. The Test GUI is cross-platform and uses
-Fox Toolkit (http://www.fox-toolkit.org). It will build on every platform
-which HIDAPI supports. Since it relies on a 3rd party library, building it
-is optional but recommended because it is so useful when debugging hardware.
-
-What Does the API Look Like?
-=============================
-The API provides the the most commonly used HID functions including sending
-and receiving of input, output, and feature reports. The sample program,
-which communicates with a heavily hacked up version of the Microchip USB
-Generic HID sample looks like this (with error checking removed for
-simplicity):
-
-#ifdef WIN32
-#include <windows.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include "hidapi.h"
-
-#define MAX_STR 255
-
-int main(int argc, char* argv[])
-{
- int res;
- unsigned char buf[65];
- wchar_t wstr[MAX_STR];
- hid_device *handle;
- int i;
-
- // Initialize the hidapi library
- res = hid_init();
-
- // Open the device using the VID, PID,
- // and optionally the Serial number.
- handle = hid_open(0x4d8, 0x3f, NULL);
-
- // Read the Manufacturer String
- res = hid_get_manufacturer_string(handle, wstr, MAX_STR);
- wprintf(L"Manufacturer String: %s\n", wstr);
-
- // Read the Product String
- res = hid_get_product_string(handle, wstr, MAX_STR);
- wprintf(L"Product String: %s\n", wstr);
-
- // Read the Serial Number String
- res = hid_get_serial_number_string(handle, wstr, MAX_STR);
- wprintf(L"Serial Number String: (%d) %s\n", wstr[0], wstr);
-
- // Read Indexed String 1
- res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
- wprintf(L"Indexed String 1: %s\n", wstr);
-
- // Toggle LED (cmd 0x80). The first byte is the report number (0x0).
- buf[0] = 0x0;
- buf[1] = 0x80;
- res = hid_write(handle, buf, 65);
-
- // Request state (cmd 0x81). The first byte is the report number (0x0).
- buf[0] = 0x0;
- buf[1] = 0x81;
- res = hid_write(handle, buf, 65);
-
- // Read requested state
- res = hid_read(handle, buf, 65);
-
- // Print out the returned buffer.
- for (i = 0; i < 4; i++)
- printf("buf[%d]: %d\n", i, buf[i]);
-
- // Finalize the hidapi library
- res = hid_exit();
-
- return 0;
-}
-
-If you have your own simple test programs which communicate with standard
-hardware development boards (such as those from Microchip, TI, Atmel,
-FreeScale and others), please consider sending me something like the above
-for inclusion into the HIDAPI source. This will help others who have the
-same hardware as you do.
-
-License
-========
-HIDAPI may be used by one of three licenses as outlined in LICENSE.txt.
-
-Download
-=========
-HIDAPI can be downloaded from github
- git clone git://github.com/libusb/hidapi.git
-
-Build Instructions
-===================
-
-This section is long. Don't be put off by this. It's not long because it's
-complicated to build HIDAPI; it's quite the opposite. This section is long
-because of the flexibility of HIDAPI and the large number of ways in which
-it can be built and used. You will likely pick a single build method.
-
-HIDAPI can be built in several different ways. If you elect to build a
-shared library, you will need to build it from the HIDAPI source
-distribution. If you choose instead to embed HIDAPI directly into your
-application, you can skip the building and look at the provided platform
-Makefiles for guidance. These platform Makefiles are located in linux/
-libusb/ mac/ and windows/ and are called Makefile-manual. In addition,
-Visual Studio projects are provided. Even if you're going to embed HIDAPI
-into your project, it is still beneficial to build the example programs.
-
-
-Prerequisites:
----------------
-
- Linux:
- -------
- On Linux, you will need to install development packages for libudev,
- libusb and optionally Fox-toolkit (for the test GUI). On
- Debian/Ubuntu systems these can be installed by running:
- sudo apt-get install libudev-dev libusb-1.0-0-dev libfox-1.6-dev
-
- If you downloaded the source directly from the git repository (using
- git clone), you'll need Autotools:
- sudo apt-get install autotools-dev autoconf automake libtool
-
- FreeBSD:
- ---------
- On FreeBSD you will need to install GNU make, libiconv, and
- optionally Fox-Toolkit (for the test GUI). This is done by running
- the following:
- pkg_add -r gmake libiconv fox16
-
- If you downloaded the source directly from the git repository (using
- git clone), you'll need Autotools:
- pkg_add -r autotools
-
- Mac:
- -----
- On Mac, you will need to install Fox-Toolkit if you wish to build
- the Test GUI. There are two ways to do this, and each has a slight
- complication. Which method you use depends on your use case.
-
- If you wish to build the Test GUI just for your own testing on your
- own computer, then the easiest method is to install Fox-Toolkit
- using ports:
- sudo port install fox
-
- If you wish to build the TestGUI app bundle to redistribute to
- others, you will need to install Fox-toolkit from source. This is
- because the version of fox that gets installed using ports uses the
- ports X11 libraries which are not compatible with the Apple X11
- libraries. If you install Fox with ports and then try to distribute
- your built app bundle, it will simply fail to run on other systems.
- To install Fox-Toolkit manually, download the source package from
- http://www.fox-toolkit.org, extract it, and run the following from
- within the extracted source:
- ./configure && make && make install
-
- Windows:
- ---------
- On Windows, if you want to build the test GUI, you will need to get
- the hidapi-externals.zip package from the download site. This
- contains pre-built binaries for Fox-toolkit. Extract
- hidapi-externals.zip just outside of hidapi, so that
- hidapi-externals and hidapi are on the same level, as shown:
-
- Parent_Folder
- |
- +hidapi
- +hidapi-externals
-
- Again, this step is not required if you do not wish to build the
- test GUI.
-
-
-Building HIDAPI into a shared library on Unix Platforms:
----------------------------------------------------------
-
-On Unix-like systems such as Linux, FreeBSD, Mac, and even Windows, using
-Mingw or Cygwin, the easiest way to build a standard system-installed shared
-library is to use the GNU Autotools build system. If you checked out the
-source from the git repository, run the following:
-
- ./bootstrap
- ./configure
- make
- make install <----- as root, or using sudo
-
-If you downloaded a source package (ie: if you did not run git clone), you
-can skip the ./bootstrap step.
-
-./configure can take several arguments which control the build. The two most
-likely to be used are:
- --enable-testgui
- Enable build of the Test GUI. This requires Fox toolkit to
- be installed. Instructions for installing Fox-Toolkit on
- each platform are in the Prerequisites section above.
-
- --prefix=/usr
- Specify where you want the output headers and libraries to
- be installed. The example above will put the headers in
- /usr/include and the binaries in /usr/lib. The default is to
- install into /usr/local which is fine on most systems.
-
-Building the manual way on Unix platforms:
--------------------------------------------
-
-Manual Makefiles are provided mostly to give the user and idea what it takes
-to build a program which embeds HIDAPI directly inside of it. These should
-really be used as examples only. If you want to build a system-wide shared
-library, use the Autotools method described above.
-
- To build HIDAPI using the manual makefiles, change to the directory
- of your platform and run make. For example, on Linux run:
- cd linux/
- make -f Makefile-manual
-
- To build the Test GUI using the manual makefiles:
- cd testgui/
- make -f Makefile-manual
-
-Building on Windows:
----------------------
-
-To build the HIDAPI DLL on Windows using Visual Studio, build the .sln file
-in the windows/ directory.
-
-To build the Test GUI on windows using Visual Studio, build the .sln file in
-the testgui/ directory.
-
-To build HIDAPI using MinGW or Cygwin using Autotools, use the instructions
-in the section titled "Building HIDAPI into a shared library on Unix
-Platforms" above. Note that building the Test GUI with MinGW or Cygwin will
-require the Windows procedure in the Prerequisites section above (ie:
-hidapi-externals.zip).
-
-To build HIDAPI using MinGW using the Manual Makefiles, see the section
-"Building the manual way on Unix platforms" above.
-
-HIDAPI can also be built using the Windows DDK (now also called the Windows
-Driver Kit or WDK). This method was originally required for the HIDAPI build
-but not anymore. However, some users still prefer this method. It is not as
-well supported anymore but should still work. Patches are welcome if it does
-not. To build using the DDK:
-
- 1. Install the Windows Driver Kit (WDK) from Microsoft.
- 2. From the Start menu, in the Windows Driver Kits folder, select Build
- Environments, then your operating system, then the x86 Free Build
- Environment (or one that is appropriate for your system).
- 3. From the console, change directory to the windows/ddk_build/ directory,
- which is part of the HIDAPI distribution.
- 4. Type build.
- 5. You can find the output files (DLL and LIB) in a subdirectory created
- by the build system which is appropriate for your environment. On
- Windows XP, this directory is objfre_wxp_x86/i386.
-
-Cross Compiling
-================
-
-This section talks about cross compiling HIDAPI for Linux using autotools.
-This is useful for using HIDAPI on embedded Linux targets. These
-instructions assume the most raw kind of embedded Linux build, where all
-prerequisites will need to be built first. This process will of course vary
-based on your embedded Linux build system if you are using one, such as
-OpenEmbedded or Buildroot.
-
-For the purpose of this section, it will be assumed that the following
-environment variables are exported.
-
- $ export STAGING=$HOME/out
- $ export HOST=arm-linux
-
-STAGING and HOST can be modified to suit your setup.
-
-Prerequisites
---------------
-
-Note that the build of libudev is the very basic configuration.
-
-Build Libusb. From the libusb source directory, run:
- ./configure --host=$HOST --prefix=$STAGING
- make
- make install
-
-Build libudev. From the libudev source directory, run:
- ./configure --disable-gudev --disable-introspection --disable-hwdb \
- --host=$HOST --prefix=$STAGING
- make
- make install
-
-Building HIDAPI
-----------------
-
-Build HIDAPI:
-
- PKG_CONFIG_DIR= \
- PKG_CONFIG_LIBDIR=$STAGING/lib/pkgconfig:$STAGING/share/pkgconfig \
- PKG_CONFIG_SYSROOT_DIR=$STAGING \
- ./configure --host=$HOST --prefix=$STAGING
-
-
-Signal 11 Software - 2010-04-11
- 2010-07-28
- 2011-09-10
- 2012-05-01
- 2012-07-03
diff --git a/src/hidapi/VERSION b/src/hidapi/VERSION
new file mode 100644
index 0000000..0548fb4
--- /dev/null
+++ b/src/hidapi/VERSION
@@ -0,0 +1 @@
+0.14.0
\ No newline at end of file
diff --git a/src/hidapi/android/hid.cpp b/src/hidapi/android/hid.cpp
deleted file mode 100644
index 450cab2..0000000
--- a/src/hidapi/android/hid.cpp
+++ /dev/null
@@ -1,1443 +0,0 @@
-/*
- Simple DirectMedia Layer
- Copyright (C) 2022 Valve Corporation
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-*/
-#include "SDL_internal.h"
-
-// Purpose: A wrapper implementing "HID" API for Android
-//
-// This layer glues the hidapi API to Android's USB and BLE stack.
-
-#include "hid.h"
-
-// Common to stub version and non-stub version of functions
-#include <jni.h>
-#include <android/log.h>
-
-#define TAG "hidapi"
-
-// Have error log always available
-#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
-
-#ifdef DEBUG
-#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
-#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
-#else
-#define LOGV(...)
-#define LOGD(...)
-#endif
-
-#define SDL_JAVA_PREFIX org_libsdl_app
-#define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function)
-#define CONCAT2(prefix, class, function) Java_ ## prefix ## _ ## class ## _ ## function
-#define HID_DEVICE_MANAGER_JAVA_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, HIDDeviceManager, function)
-
-
-#ifndef SDL_HIDAPI_DISABLED
-
-#include "../../core/android/SDL_android.h"
-
-#define hid_init PLATFORM_hid_init
-#define hid_exit PLATFORM_hid_exit
-#define hid_enumerate PLATFORM_hid_enumerate
-#define hid_free_enumeration PLATFORM_hid_free_enumeration
-#define hid_open PLATFORM_hid_open
-#define hid_open_path PLATFORM_hid_open_path
-#define hid_write PLATFORM_hid_write
-#define hid_read_timeout PLATFORM_hid_read_timeout
-#define hid_read PLATFORM_hid_read
-#define hid_set_nonblocking PLATFORM_hid_set_nonblocking
-#define hid_send_feature_report PLATFORM_hid_send_feature_report
-#define hid_get_feature_report PLATFORM_hid_get_feature_report
-#define hid_close PLATFORM_hid_close
-#define hid_get_manufacturer_string PLATFORM_hid_get_manufacturer_string
-#define hid_get_product_string PLATFORM_hid_get_product_string
-#define hid_get_serial_number_string PLATFORM_hid_get_serial_number_string
-#define hid_get_indexed_string PLATFORM_hid_get_indexed_string
-#define hid_error PLATFORM_hid_error
-
-#include <pthread.h>
-#include <errno.h> // For ETIMEDOUT and ECONNRESET
-#include <stdlib.h> // For malloc() and free()
-
-#include "../hidapi/hidapi.h"
-
-typedef uint32_t uint32;
-typedef uint64_t uint64;
-
-
-struct hid_device_
-{
- int m_nId;
- int m_nDeviceRefCount;
-};
-
-static JavaVM *g_JVM;
-static pthread_key_t g_ThreadKey;
-
-template<class T>
-class hid_device_ref
-{
-public:
- hid_device_ref( T *pObject = nullptr ) : m_pObject( nullptr )
- {
- SetObject( pObject );
- }
-
- hid_device_ref( const hid_device_ref &rhs ) : m_pObject( nullptr )
- {
- SetObject( rhs.GetObject() );
- }
-
- ~hid_device_ref()
- {
- SetObject( nullptr );
- }
-
- void SetObject( T *pObject )
- {
- if ( m_pObject && m_pObject->DecrementRefCount() == 0 )
- {
- delete m_pObject;
- }
-
- m_pObject = pObject;
-
- if ( m_pObject )
- {
- m_pObject->IncrementRefCount();
- }
- }
-
- hid_device_ref &operator =( T *pObject )
- {
- SetObject( pObject );
- return *this;
- }
-
- hid_device_ref &operator =( const hid_device_ref &rhs )
- {
- SetObject( rhs.GetObject() );
- return *this;
- }
-
- T *GetObject() const
- {
- return m_pObject;
- }
-
- T* operator->() const
- {
- return m_pObject;
- }
-
- operator bool() const
- {
- return ( m_pObject != nullptr );
- }
-
-private:
- T *m_pObject;
-};
-
-class hid_mutex_guard
-{
-public:
- hid_mutex_guard( pthread_mutex_t *pMutex ) : m_pMutex( pMutex )
- {
- pthread_mutex_lock( m_pMutex );
- }
- ~hid_mutex_guard()
- {
- pthread_mutex_unlock( m_pMutex );
- }
-
-private:
- pthread_mutex_t *m_pMutex;
-};
-
-class hid_buffer
-{
-public:
- hid_buffer() : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
- {
- }
-
- hid_buffer( const uint8_t *pData, size_t nSize ) : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
- {
- assign( pData, nSize );
- }
-
- ~hid_buffer()
- {
- delete[] m_pData;
- }
-
- void assign( const uint8_t *pData, size_t nSize )
- {
- if ( nSize > m_nAllocated )
- {
- delete[] m_pData;
- m_pData = new uint8_t[ nSize ];
- m_nAllocated = nSize;
- }
-
- m_nSize = nSize;
- SDL_memcpy( m_pData, pData, nSize );
- }
-
- void clear()
- {
- m_nSize = 0;
- }
-
- size_t size() const
- {
- return m_nSize;
- }
-
- const uint8_t *data() const
- {
- return m_pData;
- }
-
-private:
- uint8_t *m_pData;
- size_t m_nSize;
- size_t m_nAllocated;
-};
-
-class hid_buffer_pool
-{
-public:
- hid_buffer_pool() : m_nSize( 0 ), m_pHead( nullptr ), m_pTail( nullptr ), m_pFree( nullptr )
- {
- }
-
- ~hid_buffer_pool()
- {
- clear();
-
- while ( m_pFree )
- {
- hid_buffer_entry *pEntry = m_pFree;
- m_pFree = m_pFree->m_pNext;
- delete pEntry;
- }
- }
-
- size_t size() const { return m_nSize; }
-
- const hid_buffer &front() const { return m_pHead->m_buffer; }
-
- void pop_front()
- {
- hid_buffer_entry *pEntry = m_pHead;
- if ( pEntry )
- {
- m_pHead = pEntry->m_pNext;
- if ( !m_pHead )
- {
- m_pTail = nullptr;
- }
- pEntry->m_pNext = m_pFree;
- m_pFree = pEntry;
- --m_nSize;
- }
- }
-
- void emplace_back( const uint8_t *pData, size_t nSize )
- {
- hid_buffer_entry *pEntry;
-
- if ( m_pFree )
- {
- pEntry = m_pFree;
- m_pFree = m_pFree->m_pNext;
- }
- else
- {
- pEntry = new hid_buffer_entry;
- }
- pEntry->m_pNext = nullptr;
-
- if ( m_pTail )
- {
- m_pTail->m_pNext = pEntry;
- }
- else
- {
- m_pHead = pEntry;
- }
- m_pTail = pEntry;
-
- pEntry->m_buffer.assign( pData, nSize );
- ++m_nSize;
- }
-
- void clear()
- {
- while ( size() > 0 )
- {
- pop_front();
- }
- }
-
-private:
- struct hid_buffer_entry
- {
- hid_buffer m_buffer;
- hid_buffer_entry *m_pNext;
- };
-
- size_t m_nSize;
- hid_buffer_entry *m_pHead;
- hid_buffer_entry *m_pTail;
- hid_buffer_entry *m_pFree;
-};
-
-static jbyteArray NewByteArray( JNIEnv* env, const uint8_t *pData, size_t nDataLen )
-{
- jbyteArray array = env->NewByteArray( (jsize)nDataLen );
- jbyte *pBuf = env->GetByteArrayElements( array, NULL );
- SDL_memcpy( pBuf, pData, nDataLen );
- env->ReleaseByteArrayElements( array, pBuf, 0 );
-
- return array;
-}
-
-static char *CreateStringFromJString( JNIEnv *env, const jstring &sString )
-{
- size_t nLength = env->GetStringUTFLength( sString );
- const char *pjChars = env->GetStringUTFChars( sString, NULL );
- char *psString = (char*)malloc( nLength + 1 );
- SDL_memcpy( psString, pjChars, nLength );
- psString[ nLength ] = '\0';
- env->ReleaseStringUTFChars( sString, pjChars );
- return psString;
-}
-
-static wchar_t *CreateWStringFromJString( JNIEnv *env, const jstring &sString )
-{
- size_t nLength = env->GetStringLength( sString );
- const jchar *pjChars = env->GetStringChars( sString, NULL );
- wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
- wchar_t *pwChars = pwString;
- for ( size_t iIndex = 0; iIndex < nLength; ++iIndex )
- {
- pwChars[ iIndex ] = pjChars[ iIndex ];
- }
- pwString[ nLength ] = '\0';
- env->ReleaseStringChars( sString, pjChars );
- return pwString;
-}
-
-static wchar_t *CreateWStringFromWString( const wchar_t *pwSrc )
-{
- size_t nLength = SDL_wcslen( pwSrc );
- wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
- SDL_memcpy( pwString, pwSrc, nLength * sizeof( wchar_t ) );
- pwString[ nLength ] = '\0';
- return pwString;
-}
-
-static hid_device_info *CopyHIDDeviceInfo( const hid_device_info *pInfo )
-{
- hid_device_info *pCopy = new hid_device_info;
- *pCopy = *pInfo;
- pCopy->path = SDL_strdup( pInfo->path );
- pCopy->product_string = CreateWStringFromWString( pInfo->product_string );
- pCopy->manufacturer_string = CreateWStringFromWString( pInfo->manufacturer_string );
- pCopy->serial_number = CreateWStringFromWString( pInfo->serial_number );
- return pCopy;
-}
-
-static void FreeHIDDeviceInfo( hid_device_info *pInfo )
-{
- free( pInfo->path );
- free( pInfo->serial_number );
- free( pInfo->manufacturer_string );
- free( pInfo->product_string );
- delete pInfo;
-}
-
-static jclass g_HIDDeviceManagerCallbackClass;
-static jobject g_HIDDeviceManagerCallbackHandler;
-static jmethodID g_midHIDDeviceManagerInitialize;
-static jmethodID g_midHIDDeviceManagerOpen;
-static jmethodID g_midHIDDeviceManagerSendOutputReport;
-static jmethodID g_midHIDDeviceManagerSendFeatureReport;
-static jmethodID g_midHIDDeviceManagerGetFeatureReport;
-static jmethodID g_midHIDDeviceManagerClose;
-static bool g_initialized = false;
-
-static uint64_t get_timespec_ms( const struct timespec &ts )
-{
- return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
-}
-
-static void ExceptionCheck( JNIEnv *env, const char *pszClassName, const char *pszMethodName )
-{
- if ( env->ExceptionCheck() )
- {
- // Get our exception
- jthrowable jExcept = env->ExceptionOccurred();
-
- // Clear the exception so we can call JNI again
- env->ExceptionClear();
-
- // Get our exception message
- jclass jExceptClass = env->GetObjectClass( jExcept );
- jmethodID jMessageMethod = env->GetMethodID( jExceptClass, "getMessage", "()Ljava/lang/String;" );
- jstring jMessage = (jstring)( env->CallObjectMethod( jExcept, jMessageMethod ) );
- const char *pszMessage = env->GetStringUTFChars( jMessage, NULL );
-
- // ...and log it.
- LOGE( "%s%s%s threw an exception: %s",
- pszClassName ? pszClassName : "",
- pszClassName ? "::" : "",
- pszMethodName, pszMessage );
-
- // Cleanup
- env->ReleaseStringUTFChars( jMessage, pszMessage );
- env->DeleteLocalRef( jMessage );
- env->DeleteLocalRef( jExceptClass );
- env->DeleteLocalRef( jExcept );
- }
-}
-
-class CHIDDevice
-{
-public:
- CHIDDevice( int nDeviceID, hid_device_info *pInfo )
- {
- m_nId = nDeviceID;
- m_pInfo = pInfo;
-
- // The Bluetooth Steam Controller needs special handling
- const int VALVE_USB_VID = 0x28DE;
- const int D0G_BLE2_PID = 0x1106;
- if ( pInfo->vendor_id == VALVE_USB_VID && pInfo->product_id == D0G_BLE2_PID )
- {
- m_bIsBLESteamController = true;
- }
- }
-
- ~CHIDDevice()
- {
- FreeHIDDeviceInfo( m_pInfo );
-
- // Note that we don't delete m_pDevice, as the app may still have a reference to it
- }
-
- int IncrementRefCount()
- {
- int nValue;
- pthread_mutex_lock( &m_refCountLock );
- nValue = ++m_nRefCount;
- pthread_mutex_unlock( &m_refCountLock );
- return nValue;
- }
-
- int DecrementRefCount()
- {
- int nValue;
- pthread_mutex_lock( &m_refCountLock );
- nValue = --m_nRefCount;
- pthread_mutex_unlock( &m_refCountLock );
- return nValue;
- }
-
- int GetId()
- {
- return m_nId;
- }
-
- const hid_device_info *GetDeviceInfo()
- {
- return m_pInfo;
- }
-
- hid_device *GetDevice()
- {
- return m_pDevice;
- }
-
- void ExceptionCheck( JNIEnv *env, const char *pszMethodName )
- {
- ::ExceptionCheck( env, "CHIDDevice", pszMethodName );
- }
-
- bool BOpen()
- {
- // Make sure thread is attached to JVM/env
- JNIEnv *env;
- g_JVM->AttachCurrentThread( &env, NULL );
- pthread_setspecific( g_ThreadKey, (void*)env );
-
- if ( !g_HIDDeviceManagerCallbackHandler )
- {
- LOGV( "Device open without callback handler" );
- return false;
- }
-
- m_bIsWaitingForOpen = false;
- m_bOpenResult = env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerOpen, m_nId );
- ExceptionCheck( env, "BOpen" );
-
- if ( m_bIsWaitingForOpen )
- {
- hid_mutex_guard cvl( &m_cvLock );
-
- const int OPEN_TIMEOUT_SECONDS = 60;
- struct timespec ts, endtime;
- clock_gettime( CLOCK_REALTIME, &ts );
- endtime = ts;
- endtime.tv_sec += OPEN_TIMEOUT_SECONDS;
- do
- {
- if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
- {
- break;
- }
- }
- while ( m_bIsWaitingForOpen && get_timespec_ms( ts ) < get_timespec_ms( endtime ) );
- }
-
- if ( !m_bOpenResult )
- {
- if ( m_bIsWaitingForOpen )
- {
- LOGV( "Device open failed - timed out waiting for device permission" );
- }
- else
- {
- LOGV( "Device open failed" );
- }
- return false;
- }
-
- m_pDevice = new hid_device;
- m_pDevice->m_nId = m_nId;
- m_pDevice->m_nDeviceRefCount = 1;
- LOGD("Creating device %d (%p), refCount = 1\n", m_pDevice->m_nId, m_pDevice);
- return true;
- }
-
- void SetOpenPending()
- {
- m_bIsWaitingForOpen = true;
- }
-
- void SetOpenResult( bool bResult )
- {
- if ( m_bIsWaitingForOpen )
- {
- m_bOpenResult = bResult;
- m_bIsWaitingForOpen = false;
- pthread_cond_signal( &m_cv );
- }
- }
-
- void ProcessInput( const uint8_t *pBuf, size_t nBufSize )
- {
- hid_mutex_guard l( &m_dataLock );
-
- size_t MAX_REPORT_QUEUE_SIZE = 16;
- if ( m_vecData.size() >= MAX_REPORT_QUEUE_SIZE )
- {
- m_vecData.pop_front();
- }
- m_vecData.emplace_back( pBuf, nBufSize );
- }
-
- int GetInput( unsigned char *data, size_t length )
- {
- hid_mutex_guard l( &m_dataLock );
-
- if ( m_vecData.size() == 0 )
- {
-// LOGV( "hid_read_timeout no data available" );
- return 0;
- }
-
- const hid_buffer &buffer = m_vecData.front();
- size_t nDataLen = buffer.size() > length ? length : buffer.size();
- if ( m_bIsBLESteamController )
- {
- data[0] = 0x03;
- SDL_memcpy( data + 1, buffer.data(), nDataLen );
- ++nDataLen;
- }
- else
- {
- SDL_memcpy( data, buffer.data(), nDataLen );
- }
- m_vecData.pop_front();
-
-// LOGV("Read %u bytes", nDataLen);
-// LOGV("%02x %02x %02x %02x %02x %02x %02x %02x ....",
-// data[0], data[1], data[2], data[3],
-// data[4], data[5], data[6], data[7]);
-
- return (int)nDataLen;
- }
-
- int SendOutputReport( const unsigned char *pData, size_t nDataLen )
- {
- // Make sure thread is attached to JVM/env
- JNIEnv *env;
- g_JVM->AttachCurrentThread( &env, NULL );
- pthread_setspecific( g_ThreadKey, (void*)env );
-
- int nRet = -1;
- if ( g_HIDDeviceManagerCallbackHandler )
- {
- jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
- nRet = env->CallIntMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerSendOutputReport, m_nId, pBuf );
- ExceptionCheck( env, "SendOutputReport" );
- env->DeleteLocalRef( pBuf );
- }
- else
- {
- LOGV( "SendOutputReport without callback handler" );
- }
- return nRet;
- }
-
- int SendFeatureReport( const unsigned char *pData, size_t nDataLen )
- {
- // Make sure thread is attached to JVM/env
- JNIEnv *env;
- g_JVM->AttachCurrentThread( &env, NULL );
- pthread_setspecific( g_ThreadKey, (void*)env );
-
- int nRet = -1;
- if ( g_HIDDeviceManagerCallbackHandler )
- {
- jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
- nRet = env->CallIntMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerSendFeatureReport, m_nId, pBuf );
- ExceptionCheck( env, "SendFeatureReport" );
- env->DeleteLocalRef( pBuf );
- }
- else
- {
- LOGV( "SendFeatureReport without callback handler" );
- }
- return nRet;
- }
-
- void ProcessFeatureReport( const uint8_t *pBuf, size_t nBufSize )
- {
- hid_mutex_guard cvl( &m_cvLock );
- if ( m_bIsWaitingForFeatureReport )
- {
- m_featureReport.assign( pBuf, nBufSize );
-
- m_bIsWaitingForFeatureReport = false;
- m_nFeatureReportError = 0;
- pthread_cond_signal( &m_cv );
- }
- }
-
- int GetFeatureReport( unsigned char *pData, size_t nDataLen )
- {
- // Make sure thread is attached to JVM/env
- JNIEnv *env;
- g_JVM->AttachCurrentThread( &env, NULL );
- pthread_setspecific( g_ThreadKey, (void*)env );
-
- if ( !g_HIDDeviceManagerCallbackHandler )
- {
- LOGV( "GetFeatureReport without callback handler" );
- return -1;
- }
-
- {
- hid_mutex_guard cvl( &m_cvLock );
- if ( m_bIsWaitingForFeatureReport )
- {
- LOGV( "Get feature report already ongoing... bail" );
- return -1; // Read already ongoing, we currently do not serialize, TODO
- }
- m_bIsWaitingForFeatureReport = true;
- }
-
- jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
- int nRet = env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerGetFeatureReport, m_nId, pBuf ) ? 0 : -1;
- ExceptionCheck( env, "GetFeatureReport" );
- env->DeleteLocalRef( pBuf );
- if ( nRet < 0 )
- {
- LOGV( "GetFeatureReport failed" );
- m_bIsWaitingForFeatureReport = false;
- return -1;
- }
-
- {
- hid_mutex_guard cvl( &m_cvLock );
- if ( m_bIsWaitingForFeatureReport )
- {
- LOGV("=== Going to sleep" );
- // Wait in CV until we are no longer waiting for a feature report.
- const int FEATURE_REPORT_TIMEOUT_SECONDS = 2;
- struct timespec ts, endtime;
- clock_gettime( CLOCK_REALTIME, &ts );
- endtime = ts;
- endtime.tv_sec += FEATURE_REPORT_TIMEOUT_SECONDS;
- do
- {
- if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
- {
- break;
- }
- }
- while ( m_bIsWaitingForFeatureReport && get_timespec_ms( ts ) < get_timespec_ms( endtime ) );
-
- // We are back
- if ( m_bIsWaitingForFeatureReport )
- {
- m_nFeatureReportError = -ETIMEDOUT;
- m_bIsWaitingForFeatureReport = false;
- }
- LOGV( "=== Got feature report err=%d", m_nFeatureReportError );
- if ( m_nFeatureReportError != 0 )
- {
- return m_nFeatureReportError;
- }
- }
-
- size_t uBytesToCopy = m_featureReport.size() > nDataLen ? nDataLen : m_featureReport.size();
- SDL_memcpy( pData, m_featureReport.data(), uBytesToCopy );
- m_featureReport.clear();
- LOGV( "=== Got %u bytes", uBytesToCopy );
-
- return (int)uBytesToCopy;
- }
- }
-
- void Close( bool bDeleteDevice )
- {
- // Make sure thread is attached to JVM/env
- JNIEnv *env;
- g_JVM->AttachCurrentThread( &env, NULL );
- pthread_setspecific( g_ThreadKey, (void*)env );
-
- if ( g_HIDDeviceManagerCallbackHandler )
- {
- env->CallVoidMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerClose, m_nId );
- ExceptionCheck( env, "Close" );
- }
-
- hid_mutex_guard dataLock( &m_dataLock );
- m_vecData.clear();
-
- // Clean and release pending feature report reads
- hid_mutex_guard cvLock( &m_cvLock );
- m_featureReport.clear();
- m_bIsWaitingForFeatureReport = false;
- m_nFeatureReportError = -ECONNRESET;
- pthread_cond_broadcast( &m_cv );
-
- if ( bDeleteDevice )
- {
- delete m_pDevice;
- m_pDevice = nullptr;
- }
- }
-
-private:
- pthread_mutex_t m_refCountLock = PTHREAD_MUTEX_INITIALIZER;
- int m_nRefCount = 0;
- int m_nId = 0;
- hid_device_info *m_pInfo = nullptr;
- hid_device *m_pDevice = nullptr;
- bool m_bIsBLESteamController = false;
-
- pthread_mutex_t m_dataLock = PTHREAD_MUTEX_INITIALIZER; // This lock has to be held to access m_vecData
- hid_buffer_pool m_vecData;
-
- // For handling get_feature_report
- pthread_mutex_t m_cvLock = PTHREAD_MUTEX_INITIALIZER; // This lock has to be held to access any variables below
- pthread_cond_t m_cv = PTHREAD_COND_INITIALIZER;
- bool m_bIsWaitingForOpen = false;
- bool m_bOpenResult = false;
- bool m_bIsWaitingForFeatureReport = false;
- int m_nFeatureReportError = 0;
- hid_buffer m_featureReport;
-
-public:
- hid_device_ref<CHIDDevice> next;
-};
-
-class CHIDDevice;
-static pthread_mutex_t g_DevicesMutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t g_DevicesRefCountMutex = PTHREAD_MUTEX_INITIALIZER;
-static hid_device_ref<CHIDDevice> g_Devices;
-
-static hid_device_ref<CHIDDevice> FindDevice( int nDeviceId )
-{
- hid_device_ref<CHIDDevice> pDevice;
-
- hid_mutex_guard l( &g_DevicesMutex );
- for ( pDevice = g_Devices; pDevice; pDevice = pDevice->next )
- {
- if ( pDevice->GetId() == nDeviceId )
- {
- break;
- }
- }
- return pDevice;
-}
-
-static void ThreadDestroyed(void* value)
-{
- /* The thread is being destroyed, detach it from the Java VM and set the g_ThreadKey value to NULL as required */
- JNIEnv *env = (JNIEnv*) value;
- if (env != NULL) {
- g_JVM->DetachCurrentThread();
- pthread_setspecific(g_ThreadKey, NULL);
- }
-}
-
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol );
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
-
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz )
-{
- LOGV( "HIDDeviceRegisterCallback()");
-
- env->GetJavaVM( &g_JVM );
-
- /*
- * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
- * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
- */
- if (pthread_key_create(&g_ThreadKey, ThreadDestroyed) != 0) {
- __android_log_print(ANDROID_LOG_ERROR, TAG, "Error initializing pthread key");
- }
-
- if ( g_HIDDeviceManagerCallbackHandler != NULL )
- {
- env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
- g_HIDDeviceManagerCallbackClass = NULL;
- env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
- g_HIDDeviceManagerCallbackHandler = NULL;
- }
-
- g_HIDDeviceManagerCallbackHandler = env->NewGlobalRef( thiz );
- jclass objClass = env->GetObjectClass( thiz );
- if ( objClass )
- {
- g_HIDDeviceManagerCallbackClass = reinterpret_cast< jclass >( env->NewGlobalRef( objClass ) );
- g_midHIDDeviceManagerInitialize = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "initialize", "(ZZ)Z" );
- if ( !g_midHIDDeviceManagerInitialize )
- {
- __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing initialize" );
- }
- g_midHIDDeviceManagerOpen = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "openDevice", "(I)Z" );
- if ( !g_midHIDDeviceManagerOpen )
- {
- __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing openDevice" );
- }
- g_midHIDDeviceManagerSendOutputReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "sendOutputReport", "(I[B)I" );
- if ( !g_midHIDDeviceManagerSendOutputReport )
- {
- __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing sendOutputReport" );
- }
- g_midHIDDeviceManagerSendFeatureReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "sendFeatureReport", "(I[B)I" );
- if ( !g_midHIDDeviceManagerSendFeatureReport )
- {
- __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing sendFeatureReport" );
- }
- g_midHIDDeviceManagerGetFeatureReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "getFeatureReport", "(I[B)Z" );
- if ( !g_midHIDDeviceManagerGetFeatureReport )
- {
- __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing getFeatureReport" );
- }
- g_midHIDDeviceManagerClose = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "closeDevice", "(I)V" );
- if ( !g_midHIDDeviceManagerClose )
- {
- __android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing closeDevice" );
- }
- env->DeleteLocalRef( objClass );
- }
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz)
-{
- LOGV("HIDDeviceReleaseCallback");
- if ( env->IsSameObject( thiz, g_HIDDeviceManagerCallbackHandler ) )
- {
- env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
- g_HIDDeviceManagerCallbackClass = NULL;
- env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
- g_HIDDeviceManagerCallbackHandler = NULL;
- g_initialized = false;
- }
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol )
-{
- LOGV( "HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface );
-
- hid_device_info *pInfo = new hid_device_info;
- SDL_memset( pInfo, 0, sizeof( *pInfo ) );
- pInfo->path = CreateStringFromJString( env, sIdentifier );
- pInfo->vendor_id = nVendorId;
- pInfo->product_id = nProductId;
- pInfo->serial_number = CreateWStringFromJString( env, sSerialNumber );
- pInfo->release_number = nReleaseNumber;
- pInfo->manufacturer_string = CreateWStringFromJString( env, sManufacturer );
- pInfo->product_string = CreateWStringFromJString( env, sProduct );
- pInfo->interface_number = nInterface;
- pInfo->interface_class = nInterfaceClass;
- pInfo->interface_subclass = nInterfaceSubclass;
- pInfo->interface_protocol = nInterfaceProtocol;
-
- hid_device_ref<CHIDDevice> pDevice( new CHIDDevice( nDeviceID, pInfo ) );
-
- hid_mutex_guard l( &g_DevicesMutex );
- hid_device_ref<CHIDDevice> pLast, pCurr;
- for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->next )
- {
- continue;
- }
- if ( pLast )
- {
- pLast->next = pDevice;
- }
- else
- {
- g_Devices = pDevice;
- }
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID)
-{
- LOGV( "HIDDeviceOpenPending() id=%d\n", nDeviceID );
- hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
- if ( pDevice )
- {
- pDevice->SetOpenPending();
- }
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened)
-{
- LOGV( "HIDDeviceOpenResult() id=%d, result=%s\n", nDeviceID, bOpened ? "true" : "false" );
- hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
- if ( pDevice )
- {
- pDevice->SetOpenResult( bOpened );
- }
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID)
-{
- LOGV( "HIDDeviceDisconnected() id=%d\n", nDeviceID );
- hid_device_ref<CHIDDevice> pDevice;
- {
- hid_mutex_guard l( &g_DevicesMutex );
- hid_device_ref<CHIDDevice> pLast, pCurr;
- for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->next )
- {
- if ( pCurr->GetId() == nDeviceID )
- {
- pDevice = pCurr;
-
- if ( pLast )
- {
- pLast->next = pCurr->next;
- }
- else
- {
- g_Devices = pCurr->next;
- }
- }
- }
- }
- if ( pDevice )
- {
- pDevice->Close( false );
- }
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
-{
- jbyte *pBuf = env->GetByteArrayElements(value, NULL);
- jsize nBufSize = env->GetArrayLength(value);
-
-// LOGV( "HIDDeviceInput() id=%d len=%u\n", nDeviceID, nBufSize );
- hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
- if ( pDevice )
- {
- pDevice->ProcessInput( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
- }
-
- env->ReleaseByteArrayElements(value, pBuf, 0);
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
-{
- jbyte *pBuf = env->GetByteArrayElements(value, NULL);
- jsize nBufSize = env->GetArrayLength(value);
-
- LOGV( "HIDDeviceFeatureReport() id=%d len=%u\n", nDeviceID, nBufSize );
- hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
- if ( pDevice )
- {
- pDevice->ProcessFeatureReport( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
- }
-
- env->ReleaseByteArrayElements(value, pBuf, 0);
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-extern "C"
-{
-
-int hid_init(void)
-{
- if ( !g_initialized )
- {
- // HIDAPI doesn't work well with Android < 4.3
- if (SDL_GetAndroidSDKVersion() >= 18) {
- // Make sure thread is attached to JVM/env
- JNIEnv *env;
- g_JVM->AttachCurrentThread( &env, NULL );
- pthread_setspecific( g_ThreadKey, (void*)env );
-
- if ( !g_HIDDeviceManagerCallbackHandler )
- {
- LOGV( "hid_init() without callback handler" );
- return -1;
- }
-
- // Bluetooth is currently only used for Steam Controllers, so check that hint
- // before initializing Bluetooth, which will prompt the user for permission.
- bool init_usb = true;
- bool init_bluetooth = false;
- if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_STEAM, SDL_FALSE)) {
- if (SDL_GetAndroidSDKVersion() < 31 ||
- Android_JNI_RequestPermission("android.permission.BLUETOOTH_CONNECT")) {
- init_bluetooth = true;
- }
- }
- env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerInitialize, init_usb, init_bluetooth );
- ExceptionCheck( env, NULL, "hid_init" );
- }
- g_initialized = true; // Regardless of result, so it's only called once
- }
- return 0;
-}
-
-struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
-{
- struct hid_device_info *root = NULL;
- const char *hint = SDL_GetHint(SDL_HINT_HIDAPI_IGNORE_DEVICES);
-
- hid_mutex_guard l( &g_DevicesMutex );
- for ( hid_device_ref<CHIDDevice> pDevice = g_Devices; pDevice; pDevice = pDevice->next )
- {
- const hid_device_info *info = pDevice->GetDeviceInfo();
-
- /* See if there are any devices we should skip in enumeration */
- if (hint) {
- char vendor_match[16], product_match[16];
- SDL_snprintf(vendor_match, sizeof(vendor_match), "0x%.4x/0x0000", info->vendor_id);
- SDL_snprintf(product_match, sizeof(product_match), "0x%.4x/0x%.4x", info->vendor_id, info->product_id);
- if (SDL_strcasestr(hint, vendor_match) || SDL_strcasestr(hint, product_match)) {
- continue;
- }
- }
-
- if ( ( vendor_id == 0x0 || info->vendor_id == vendor_id ) &&
- ( product_id == 0x0 || info->product_id == product_id ) )
- {
- hid_device_info *dev = CopyHIDDeviceInfo( info );
- dev->next = root;
- root = dev;
- }
- }
- return root;
-}
-
-void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
-{
- while ( devs )
- {
- struct hid_device_info *next = devs->next;
- FreeHIDDeviceInfo( devs );
- devs = next;
- }
-}
-
-HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
-{
- // TODO: Implement
- return NULL;
-}
-
-HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bExclusive)
-{
- LOGV( "hid_open_path( %s )", path );
-
- hid_device_ref< CHIDDevice > pDevice;
- {
- hid_mutex_guard r( &g_DevicesRefCountMutex );
- hid_mutex_guard l( &g_DevicesMutex );
- for ( hid_device_ref<CHIDDevice> pCurr = g_Devices; pCurr; pCurr = pCurr->next )
- {
- if ( SDL_strcmp( pCurr->GetDeviceInfo()->path, path ) == 0 )
- {
- hid_device *pValue = pCurr->GetDevice();
- if ( pValue )
- {
- ++pValue->m_nDeviceRefCount;
- LOGD("Incrementing device %d (%p), refCount = %d\n", pValue->m_nId, pValue, pValue->m_nDeviceRefCount);
- return pValue;
- }
-
- // Hold a shared pointer to the controller for the duration
- pDevice = pCurr;
- break;
- }
- }
- }
- if ( pDevice && pDevice->BOpen() )
- {
- return pDevice->GetDevice();
- }
- return NULL;
-}
-
-int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length)
-{
- if ( device )
- {
- LOGV( "hid_write id=%d length=%u", device->m_nId, length );
- hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
- if ( pDevice )
- {
- return pDevice->SendOutputReport( data, length );
- }
- }
- return -1; // Controller was disconnected
-}
-
-static uint32_t getms()
-{
- struct timeval now;
-
- gettimeofday(&now, NULL);
- return (uint32_t)(now.tv_sec * 1000 + now.tv_usec / 1000);
-}
-
-static void delayms(uint32_t ms)
-{
- int was_error;
-
- struct timespec elapsed, tv;
-
- /* Set the timeout interval */
- elapsed.tv_sec = ms / 1000;
- elapsed.tv_nsec = (ms % 1000) * 1000000;
- do {
- errno = 0;
-
- tv.tv_sec = elapsed.tv_sec;
- tv.tv_nsec = elapsed.tv_nsec;
- was_error = nanosleep(&tv, &elapsed);
- } while (was_error && (errno == EINTR));
-}
-
-int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
-{
- if ( device )
- {
-// LOGV( "hid_read_timeout id=%d length=%u timeout=%d", device->m_nId, length, milliseconds );
- hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
- if ( pDevice )
- {
- int nResult = pDevice->GetInput( data, length );
- if ( nResult == 0 && milliseconds > 0 )
- {
- uint32_t start = getms();
- do
- {
- delayms( 1 );
- nResult = pDevice->GetInput( data, length );
- } while ( nResult == 0 && ( getms() - start ) < milliseconds );
- }
- return nResult;
- }
- LOGV( "controller was disconnected" );
- }
- return -1; // Controller was disconnected
-}
-
-// TODO: Implement blocking
-int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length)
-{
- LOGV( "hid_read id=%d length=%u", device->m_nId, length );
- return hid_read_timeout( device, data, length, 0 );
-}
-
-// TODO: Implement?
-int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock)
-{
- return -1;
-}
-
-int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length)
-{
- if ( device )
- {
- LOGV( "hid_send_feature_report id=%d length=%u", device->m_nId, length );
- hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
- if ( pDevice )
- {
- return pDevice->SendFeatureReport( data, length );
- }
- }
- return -1; // Controller was disconnected
-}
-
-
-// Synchronous operation. Will block until completed.
-int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length)
-{
- if ( device )
- {
- LOGV( "hid_get_feature_report id=%d length=%u", device->m_nId, length );
- hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
- if ( pDevice )
- {
- return pDevice->GetFeatureReport( data, length );
- }
- }
- return -1; // Controller was disconnected
-}
-
-
-void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
-{
- if ( device )
- {
- LOGV( "hid_close id=%d", device->m_nId );
- hid_mutex_guard r( &g_DevicesRefCountMutex );
- LOGD("Decrementing device %d (%p), refCount = %d\n", device->m_nId, device, device->m_nDeviceRefCount - 1);
- if ( --device->m_nDeviceRefCount == 0 )
- {
- hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
- if ( pDevice )
- {
- pDevice->Close( true );
- }
- else
- {
- delete device;
- }
- LOGD("Deleted device %p\n", device);
- }
- }
-}
-
-int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen)
-{
- if ( device )
- {
- hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
- if ( pDevice )
- {
- wcsncpy( string, pDevice->GetDeviceInfo()->manufacturer_string, maxlen );
- return 0;
- }
- }
- return -1;
-}
-
-int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen)
-{
- if ( device )
- {
- hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
- if ( pDevice )
- {
- wcsncpy( string, pDevice->GetDeviceInfo()->product_string, maxlen );
- return 0;
- }
- }
- return -1;
-}
-
-int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen)
-{
- if ( device )
- {
- hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
- if ( pDevice )
- {
- wcsncpy( string, pDevice->GetDeviceInfo()->serial_number, maxlen );
- return 0;
- }
- }
- return -1;
-}
-
-int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen)
-{
- return -1;
-}
-
-HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device)
-{
- return NULL;
-}
-
-int hid_exit(void)
-{
- return 0;
-}
-
-}
-
-#else
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol );
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
-
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz )
-{
- LOGV("Stub HIDDeviceRegisterCallback()");
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz)
-{
- LOGV("Stub HIDDeviceReleaseCallback()");
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol )
-{
- LOGV("Stub HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface);
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID)
-{
- LOGV("Stub HIDDeviceOpenPending() id=%d\n", nDeviceID);
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened)
-{
- LOGV("Stub HIDDeviceOpenResult() id=%d, result=%s\n", nDeviceID, bOpened ? "true" : "false");
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID)
-{
- LOGV("Stub HIDDeviceDisconnected() id=%d\n", nDeviceID);
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
-{
- LOGV("Stub HIDDeviceInput() id=%d len=%u\n", nDeviceID, nBufSize);
-}
-
-extern "C"
-JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
-{
- LOGV("Stub HIDDeviceFeatureReport() id=%d len=%u\n", nDeviceID, nBufSize);
-}
-
-#endif /* SDL_HIDAPI_DISABLED */
-
-extern "C"
-JNINativeMethod HIDDeviceManager_tab[8] = {
- { "HIDDeviceRegisterCallback", "()V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback) },
- { "HIDDeviceReleaseCallback", "()V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback) },
- { "HIDDeviceConnected", "(ILjava/lang/String;IILjava/lang/String;ILjava/lang/String;Ljava/lang/String;IIII)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected) },
- { "HIDDeviceOpenPending", "(I)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending) },
- { "HIDDeviceOpenResult", "(IZ)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult) },
- { "HIDDeviceDisconnected", "(I)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected) },
- { "HIDDeviceInputReport", "(I[B)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport) },
- { "HIDDeviceFeatureReport", "(I[B)V", (void*)HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport) }
-};
diff --git a/src/hidapi/android/hid.h b/src/hidapi/android/hid.h
deleted file mode 100644
index 5e6253b..0000000
--- a/src/hidapi/android/hid.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- Simple DirectMedia Layer
- Copyright (C) 2022 Valve Corporation
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-*/
-
-// Purpose: Exporting table containing HIDDeviceManager native methods
-
-#ifndef SDL_android_hid_h_
-#define SDL_android_hid_h_
-
-#include <jni.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern JNINativeMethod HIDDeviceManager_tab[8];
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/src/hidapi/android/jni/Android.mk b/src/hidapi/android/jni/Android.mk
deleted file mode 100644
index 4462e88..0000000
--- a/src/hidapi/android/jni/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-HIDAPI_ROOT_REL:= ../..
-HIDAPI_ROOT_ABS:= $(LOCAL_PATH)/../..
-
-include $(CLEAR_VARS)
-
-LOCAL_CPPFLAGS += -std=c++11
-
-LOCAL_SRC_FILES := \
- $(HIDAPI_ROOT_REL)/android/hid.cpp
-
-LOCAL_MODULE := libhidapi
-LOCAL_LDLIBS := -llog
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/src/hidapi/android/jni/Application.mk b/src/hidapi/android/jni/Application.mk
deleted file mode 100644
index 4fc6ba5..0000000
--- a/src/hidapi/android/jni/Application.mk
+++ /dev/null
@@ -1,2 +0,0 @@
-APP_STL := gnustl_static
-APP_ABI := armeabi-v7a
diff --git a/src/hidapi/android/project.properties b/src/hidapi/android/project.properties
deleted file mode 100644
index 6e18427..0000000
--- a/src/hidapi/android/project.properties
+++ /dev/null
@@ -1,14 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-21
diff --git a/src/hidapi/configure.ac b/src/hidapi/configure.ac
index c6747f9..cc7ceac 100644
--- a/src/hidapi/configure.ac
+++ b/src/hidapi/configure.ac
@@ -1,13 +1,9 @@
AC_PREREQ(2.63)
-# Version number. This is currently the only place.
-m4_define([HIDAPI_MAJOR], 0)
-m4_define([HIDAPI_MINOR], 8)
-m4_define([HIDAPI_RELEASE], 0)
-m4_define([HIDAPI_RC], -rc1)
-m4_define([VERSION_STRING], HIDAPI_MAJOR[.]HIDAPI_MINOR[.]HIDAPI_RELEASE[]HIDAPI_RC)
+AC_INIT([hidapi],[m4_normalize(m4_builtin([include], VERSION))],[https://github.com/libusb/hidapi/issues])
-AC_INIT([hidapi],[VERSION_STRING],[alan@signal11.us])
+echo "This build script for HIDAPI is deprecated."
+echo "Consider using CMake instead."
# Library soname version
# Follow the following rules (particularly the ones in the second link):
@@ -20,7 +16,6 @@
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([foreign -Wall -Werror])
-AC_CONFIG_MACRO_DIR([m4])
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
LT_INIT
@@ -63,14 +58,14 @@
# HIDAPI/hidraw libs
PKG_CHECK_MODULES([libudev], [libudev], true, [hidapi_lib_error libudev])
- LIBS_HIDRAW_PR+=" $libudev_LIBS"
- CFLAGS_HIDRAW+=" $libudev_CFLAGS"
+ LIBS_HIDRAW_PR="${LIBS_HIDRAW_PR} $libudev_LIBS"
+ CFLAGS_HIDRAW="${CFLAGS_HIDRAW} $libudev_CFLAGS"
# HIDAPI/libusb libs
- AC_CHECK_LIB([rt], [clock_gettime], [LIBS_LIBUSB_PRIVATE+=" -lrt"], [hidapi_lib_error librt])
+ AC_CHECK_LIB([rt], [clock_gettime], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -lrt"], [hidapi_lib_error librt])
PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0])
- LIBS_LIBUSB_PRIVATE+=" $libusb_LIBS"
- CFLAGS_LIBUSB+=" $libusb_CFLAGS"
+ LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} $libusb_LIBS"
+ CFLAGS_LIBUSB="${CFLAGS_LIBUSB} $libusb_CFLAGS"
;;
*-darwin*)
AC_MSG_RESULT([ (Mac OS X back-end)])
@@ -79,7 +74,7 @@
backend="mac"
os="darwin"
threads="pthreads"
- LIBS="${LIBS} -framework IOKit -framework CoreFoundation"
+ LIBS="${LIBS} -framework IOKit -framework CoreFoundation -framework AppKit"
;;
*-freebsd*)
AC_MSG_RESULT([ (FreeBSD back-end)])
@@ -92,9 +87,10 @@
CFLAGS="$CFLAGS -I/usr/local/include"
LDFLAGS="$LDFLAGS -L/usr/local/lib"
LIBS="${LIBS}"
- AC_CHECK_LIB([usb], [libusb_init], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -lusb"], [hidapi_lib_error libusb])
+ PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0])
+ LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} $libusb_LIBS"
+ CFLAGS_LIBUSB="${CFLAGS_LIBUSB} $libusb_CFLAGS"
AC_CHECK_LIB([iconv], [iconv_open], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -liconv"], [hidapi_lib_error libiconv])
- echo libs_priv: $LIBS_LIBUSB_PRIVATE
;;
*-kfreebsd*)
AC_MSG_RESULT([ (kFreeBSD back-end)])
@@ -104,8 +100,22 @@
os="kfreebsd"
threads="pthreads"
- AC_CHECK_LIB([usb], [libusb_init], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -lusb"], [hidapi_lib_error libusb])
- echo libs_priv: $LIBS_LIBUSB_PRIVATE
+ PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0])
+ LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} $libusb_LIBS"
+ CFLAGS_LIBUSB="${CFLAGS_LIBUSB} $libusb_CFLAGS"
+ ;;
+*-*-haiku)
+ AC_MSG_RESULT([ (Haiku back-end)])
+ AC_DEFINE(OS_HAIKU, 1, [Haiku implementation])
+ AC_SUBST(OS_HAIKU)
+ backend="libusb"
+ os="haiku"
+ threads="pthreads"
+
+ PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0])
+ LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} $libusb_LIBS"
+ CFLAGS_LIBUSB="${CFLAGS_LIBUSB} $libusb_CFLAGS"
+ AC_CHECK_LIB([iconv], [libiconv_open], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -liconv"], [hidapi_lib_error libiconv])
;;
*-mingw*)
AC_MSG_RESULT([ (Windows back-end, using MinGW)])
@@ -113,6 +123,15 @@
os="windows"
threads="windows"
win_implementation="mingw"
+ LDFLAGS="${LDFLAGS} -static-libgcc"
+ ;;
+*-msys*)
+ AC_MSG_RESULT([ (Windows back-end, using MSYS2)])
+ backend="windows"
+ os="windows"
+ threads="windows"
+ win_implementation="mingw"
+ LDFLAGS="${LDFLAGS} -static-libgcc"
;;
*-cygwin*)
AC_MSG_RESULT([ (Windows back-end, using Cygwin)])
@@ -136,7 +155,7 @@
AC_DEFINE(OS_WINDOWS, 1, [Windows implementations])
AC_SUBST(OS_WINDOWS)
LDFLAGS="${LDFLAGS} -no-undefined"
- LIBS="${LIBS} -lsetupapi"
+ LIBS="${LIBS}"
fi
if test "x$threads" = xpthreads; then
@@ -175,15 +194,15 @@
if test "x$testgui_enabled" != "xno"; then
if test "x$os" = xdarwin; then
- # On Mac OS, don't use pkg-config.
+ # On Mac OS, do not use pkg-config.
AC_CHECK_PROG([foxconfig], [fox-config], [fox-config], false)
if test "x$foxconfig" = "xfalse"; then
hidapi_prog_error fox-config "FOX Toolkit"
fi
- LIBS_TESTGUI+=`$foxconfig --libs`
- LIBS_TESTGUI+=" -framework Cocoa -L/usr/X11R6/lib"
- CFLAGS_TESTGUI+=`$foxconfig --cflags`
- OBJCFLAGS+=" -x objective-c++"
+ LIBS_TESTGUI="${LIBS_TESTGUI} `$foxconfig --libs`"
+ LIBS_TESTGUI="${LIBS_TESTGUI} -framework Cocoa -L/usr/X11R6/lib"
+ CFLAGS_TESTGUI="${CFLAGS_TESTGUI} `$foxconfig --cflags`"
+ OBJCFLAGS="${OBJCFLAGS} -x objective-c++"
elif test "x$os" = xwindows; then
# On Windows, just set the paths for Fox toolkit
if test "x$win_implementation" = xmingw; then
@@ -213,6 +232,7 @@
AM_CONDITIONAL(OS_DARWIN, test "x$os" = xdarwin)
AM_CONDITIONAL(OS_FREEBSD, test "x$os" = xfreebsd)
AM_CONDITIONAL(OS_KFREEBSD, test "x$os" = xkfreebsd)
+AM_CONDITIONAL(OS_HAIKU, test "x$os" = xhaiku)
AM_CONDITIONAL(OS_WINDOWS, test "x$os" = xwindows)
AC_CONFIG_HEADERS([config.h])
diff --git a/src/hidapi/dist/hidapi.podspec b/src/hidapi/dist/hidapi.podspec
new file mode 100644
index 0000000..74642ef
--- /dev/null
+++ b/src/hidapi/dist/hidapi.podspec
@@ -0,0 +1,31 @@
+Pod::Spec.new do |spec|
+
+ spec.name = "hidapi"
+ spec.version = File.read('../VERSION')
+ spec.summary = "A Simple library for communicating with USB and Bluetooth HID devices on Linux, Mac and Windows."
+
+ spec.description = <<-DESC
+ HIDAPI is a multi-platform library which allows an application to interface with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and macOS. HIDAPI can be either built as a shared library (.so, .dll or .dylib) or can be embedded directly into a target application by adding a single source file (per platform) and a single header.
+ DESC
+
+ spec.homepage = "https://github.com/libusb/hidapi"
+
+ spec.license = { :type=> "GNU GPLv3 or BSD or HIDAPI original", :file => "LICENSE.txt" }
+
+ spec.authors = { "Alan Ott" => "alan@signal11.us",
+ "Ludovic Rousseau" => "rousseau@debian.org",
+ "libusb/hidapi Team" => "https://github.com/libusb/hidapi/blob/master/AUTHORS.txt",
+ }
+
+ spec.platform = :osx
+ spec.osx.deployment_target = "10.7"
+
+ spec.source = { :git => "https://github.com/libusb/hidapi.git", :tag => "hidapi-#{spec.version}" }
+
+ spec.source_files = "mac/hid.c", "hidapi/hidapi.h", "mac/hidapi_darwin.h"
+
+ spec.public_header_files = "hidapi/hidapi.h", "mac/hidapi_darwin.h"
+
+ spec.frameworks = "IOKit", "CoreFoundation", "AppKit"
+
+end
diff --git a/src/hidapi/documentation/cmake-gui-drop-down.png b/src/hidapi/documentation/cmake-gui-drop-down.png
new file mode 100644
index 0000000..548abe8
--- /dev/null
+++ b/src/hidapi/documentation/cmake-gui-drop-down.png
Binary files differ
diff --git a/src/hidapi/documentation/cmake-gui-highlights.png b/src/hidapi/documentation/cmake-gui-highlights.png
new file mode 100644
index 0000000..228838f
--- /dev/null
+++ b/src/hidapi/documentation/cmake-gui-highlights.png
Binary files differ
diff --git a/src/hidapi/doxygen/Doxyfile b/src/hidapi/doxygen/Doxyfile
index 9d983e9..4e01360 100644
--- a/src/hidapi/doxygen/Doxyfile
+++ b/src/hidapi/doxygen/Doxyfile
@@ -1,96 +1,151 @@
-# Doxyfile 1.7.1
+# Doxyfile 1.9.6
# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
+# doxygen (www.doxygen.org) for a project.
#
-# All text after a hash (#) is considered a comment and will be ignored
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
# The format is:
-# TAG = value [value, ...]
-# For lists items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+#
+# Note:
+#
+# Use doxygen to compare the used configuration file with the template
+# configuration file:
+# doxygen -x [configFile]
+# Use doxygen to compare the used configuration file with the template
+# configuration file without replacing the environment variables or CMake type
+# replacement variables:
+# doxygen -x_noenv [configFile]
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
PROJECT_NAME = hidapi
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
PROJECT_NUMBER =
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
OUTPUT_DIRECTORY =
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
+# sub-directories (in 2 levels) under the output directory of each output format
+# and will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
+# control the number of sub-directories.
+# The default value is: NO.
CREATE_SUBDIRS = NO
+# Controls the number of sub-directories that will be created when
+# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
+# level increment doubles the number of directories, resulting in 4096
+# directories at level 8 which is the default and also the maximum value. The
+# sub-directories are organized in 2 levels, the first level always has a fixed
+# number of 16 directories.
+# Minimum value: 0, maximum value: 8, default value: 8.
+# This tag requires that the tag CREATE_SUBDIRS is set to YES.
+
+CREATE_SUBDIRS_LEVEL = 8
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
-# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
+# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
+# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
+# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
+# English messages), Korean, Korean-en (Korean with English messages), Latvian,
+# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
+# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
+# Swedish, Turkish, Ukrainian and Vietnamese.
+# The default value is: English.
OUTPUT_LANGUAGE = English
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
BRIEF_MEMBER_DESC = YES
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
+# The default value is: YES.
REPEAT_BRIEF = YES
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
+# doxygen will generate a detailed section even if there is only a brief
# description.
+# The default value is: NO.
ALWAYS_DETAILED_SEC = NO
@@ -98,531 +153,831 @@
# inherited members of a class in the documentation of that class as if those
# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
+# The default value is: NO.
INLINE_INHERITED_MEMB = NO
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
FULL_PATH_NAMES = YES
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-STRIP_FROM_PATH =
+STRIP_FROM_PATH = ../
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
STRIP_FROM_INC_PATH =
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful is your file systems
-# doesn't support long names like on DOS, Mac, or CD-ROM.
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
SHORT_NAMES = NO
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
JAVADOC_AUTOBRIEF = NO
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
QT_AUTOBRIEF = NO
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
MULTILINE_CPP_IS_BRIEF = NO
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
+# By default Python docstrings are displayed as preformatted text and doxygen's
+# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
+# doxygen's special commands can be used and the contents of the docstring
+# documentation blocks is shown as doxygen documentation.
+# The default value is: YES.
+
+PYTHON_DOCSTRING = YES
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
INHERIT_DOCS = YES
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
SEPARATE_MEMBER_PAGES = NO
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
TAB_SIZE = 8
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:^^"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". Note that you cannot put \n's in the value part of an alias
+# to insert newlines (in the resulting output). You can put ^^ in the value part
+# of an alias to insert a newline as if a physical newline was in the original
+# file. When you need a literal { or } or , in the value part of an alias you
+# have to escape them by means of a backslash (\), this can lead to conflicts
+# with the commands \{ and \} for these it is advised to use the version @{ and
+# @} or use a double escape (\\{ and \\})
ALIASES =
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = YES
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
OPTIMIZE_FOR_FORTRAN = NO
# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
OPTIMIZE_OUTPUT_VHDL = NO
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE = NO
+
# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this
-# tag. The format is ext=language, where ext is a file extension, and language
-# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
-# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
-# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
-# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen. When specifying no_extension you should add
+# * to the FILE_PATTERNS.
+#
+# Note see also the list of default file extension mappings.
EXTENSION_MAPPING =
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = NO
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 5.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 5
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also make the inheritance and collaboration
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
BUILTIN_STL_SUPPORT = NO
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
+# The default value is: NO.
CPP_CLI_SUPPORT = NO
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
SIP_SUPPORT = NO
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen to replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
IDL_PROPERTY_SUPPORT = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
+# tag is set to YES then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
+# The default value is: NO.
DISTRIBUTE_GROUP_DOC = NO
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
SUBGROUPING = YES
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
TYPEDEF_HIDES_STRUCT = NO
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penality.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will rougly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
-SYMBOL_CACHE_SIZE = 0
+LOOKUP_CACHE_SIZE = 0
+
+# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
+# during processing. When set to 0 doxygen will based this on the number of
+# cores available in the system. You can set it explicitly to a value larger
+# than 0 to get more control over the balance between CPU load and processing
+# speed. At this moment only the input processing can be done using multiple
+# threads. Since this is still an experimental feature the default is set to 1,
+# which effectively disables parallel processing. Please report any issues you
+# encounter. Generating dot graphs in parallel is controlled by the
+# DOT_NUM_THREADS setting.
+# Minimum value: 0, maximum value: 32, default value: 1.
+
+NUM_PROC_THREADS = 1
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
EXTRACT_ALL = NO
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
EXTRACT_PRIVATE = NO
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
EXTRACT_STATIC = NO
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
EXTRACT_LOCAL_CLASSES = YES
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
EXTRACT_LOCAL_METHODS = NO
# If this flag is set to YES, the members of anonymous namespaces will be
# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespace are hidden.
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
EXTRACT_ANON_NSPACES = NO
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
+# If this flag is set to YES, the name of an unnamed parameter in a declaration
+# will be determined by the corresponding definition. By default unnamed
+# parameters remain unnamed in the output.
+# The default value is: YES.
+
+RESOLVE_UNNAMED_PARAMS = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
HIDE_UNDOC_MEMBERS = NO
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# will also hide undocumented C++ concepts if enabled. This option has no effect
+# if EXTRACT_ALL is enabled.
+# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# declarations. If set to NO, these declarations will be included in the
# documentation.
+# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
HIDE_IN_BODY_DOCS = NO
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
INTERNAL_DOCS = NO
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
+# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
+# able to match the capabilities of the underlying filesystem. In case the
+# filesystem is case sensitive (i.e. it supports files in the same directory
+# whose names only differ in casing), the option must be set to YES to properly
+# deal with such files in case they appear in the input. For filesystems that
+# are not case sensitive the option should be set to NO to properly deal with
+# output files written for symbols that only differ in casing, such as for two
+# classes, one named CLASS and the other named Class, and to also support
+# references to files without having to specify the exact matching casing. On
+# Windows (including Cygwin) and MacOS, users should typically set this option
+# to NO, whereas on Linux or other Unix flavors it should typically be set to
+# YES.
+# Possible values are: SYSTEM, NO and YES.
+# The default value is: SYSTEM.
CASE_SENSE_NAMES = YES
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
HIDE_SCOPE_NAMES = NO
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
+# will show which file needs to be included to use the class.
+# The default value is: YES.
+
+SHOW_HEADERFILE = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
SHOW_INCLUDE_FILES = YES
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
FORCE_LOCAL_INCLUDES = NO
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
INLINE_INFO = YES
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
SORT_MEMBER_DOCS = YES
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
SORT_BRIEF_DOCS = NO
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
-# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
SORT_MEMBERS_CTORS_1ST = NO
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
SORT_GROUP_NAMES = NO
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
SORT_BY_SCOPE_NAME = NO
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
GENERATE_TODOLIST = YES
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
GENERATE_TESTLIST = YES
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
GENERATE_BUGLIST = YES
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
GENERATE_DEPRECATEDLIST= YES
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
ENABLED_SECTIONS =
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or define consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and defines in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
MAX_INITIALIZER_LINES = 30
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
SHOW_USED_FILES = YES
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES = NO
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
SHOW_FILES = YES
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.
-# This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. The create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
-# DoxygenLayout.xml will be used as the name of the layout file.
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file. See also section "Changing the
+# layout of pages" for information.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
LAYOUT_FILE =
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
+# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
WARNINGS = YES
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
WARN_IF_UNDOCUMENTED = YES
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as documenting some parameters in
+# a documented function twice, or documenting parameters that don't exist or
+# using markup commands wrongly.
+# The default value is: YES.
WARN_IF_DOC_ERROR = YES
-# This WARN_NO_PARAMDOC option can be abled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
+# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
+# function parameter documentation. If set to NO, doxygen will accept that some
+# parameters have no documentation without warning.
+# The default value is: YES.
+
+WARN_IF_INCOMPLETE_DOC = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong parameter
+# documentation, but not about the absence of documentation. If EXTRACT_ALL is
+# set to YES then this flag will automatically be disabled. See also
+# WARN_IF_INCOMPLETE_DOC
+# The default value is: NO.
WARN_NO_PARAMDOC = NO
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
+# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
+# undocumented enumeration values. If set to NO, doxygen will accept
+# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: NO.
+
+WARN_IF_UNDOC_ENUM_VAL = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
+# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
+# at the end of the doxygen process doxygen will return with a non-zero status.
+# Possible values are: NO, YES and FAIL_ON_WARNINGS.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# See also: WARN_LINE_FORMAT
+# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
+# In the $text part of the WARN_FORMAT command it is possible that a reference
+# to a more specific place is given. To make it easier to jump to this place
+# (outside of doxygen) the user can define a custom "cut" / "paste" string.
+# Example:
+# WARN_LINE_FORMAT = "'vi $file +$line'"
+# See also: WARN_FORMAT
+# The default value is: at line $line of file $file.
+
+WARN_LINE_FORMAT = "at line $line of file $file"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr). In case the file specified cannot be opened for writing the
+# warning and error messages are written to standard error. When as file - is
+# specified the warning and error messages are written to standard output
+# (stdout).
WARN_LOGFILE =
#---------------------------------------------------------------------------
-# configuration options related to the input files
+# Configuration options related to the input files
#---------------------------------------------------------------------------
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
-INPUT = ../hidapi
+INPUT = ../hidapi \
+ ./main_page.md
# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see:
+# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
+# See also: INPUT_FILE_ENCODING
+# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
+# character encoding on a per file pattern basis. Doxygen will compare the file
+# name with each pattern and apply the encoding instead of the default
+# INPUT_ENCODING) if there is a match. The character encodings are a list of the
+# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
+# "INPUT_ENCODING" for further information on supported encodings.
+
+INPUT_FILE_ENCODING =
+
# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# Note the list of default checked file patterns might differ from the list of
+# default file extension mappings.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
+# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
+# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
+# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS =
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
RECURSIVE = NO
-# The EXCLUDE tag can be used to specify files and/or directories that should
+# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
EXCLUDE =
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
-# directories that are symbolic links (a Unix filesystem feature) are excluded
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
+# The default value is: NO.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS =
@@ -630,689 +985,1278 @@
# (namespaces, classes, functions, etc.) that should be excluded from the
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
+# ANamespace::AClass, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
-EXCLUDE_SYMBOLS =
+EXCLUDE_SYMBOLS = HID_API_AS_STR_IMPL \
+ HID_API_AS_STR \
+ HID_API_TO_VERSION_STR
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
-EXAMPLE_PATH =
+EXAMPLE_PATH = ../hidtest
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
-EXAMPLE_PATTERNS =
+EXAMPLE_PATTERNS = *.c
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
EXAMPLE_RECURSIVE = NO
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
-# If FILTER_PATTERNS is specified, this tag will be
-# ignored.
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that doxygen will use the data processed and written to standard output
+# for further processing, therefore nothing else, like debug statements or used
+# commands (so in case of a Windows batch file always use @echo OFF), should be
+# written to standard output.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.
-# Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.
-# The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
-# is applied to all files.
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
FILTER_SOURCE_FILES = NO
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = main_page.md
+
+# The Fortran standard specifies that for fixed formatted Fortran code all
+# characters from position 72 are to be considered as comment. A common
+# extension is to allow longer lines before the automatic comment starts. The
+# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
+# be processed before the automatic comment starts.
+# Minimum value: 7, maximum value: 10000, default value: 72.
+
+FORTRAN_COMMENT_AFTER = 72
+
#---------------------------------------------------------------------------
-# configuration options related to source browsing
+# Configuration options related to source browsing
#---------------------------------------------------------------------------
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
SOURCE_BROWSER = NO
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
INLINE_SOURCES = NO
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
STRIP_CODE_COMMENTS = YES
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
REFERENCED_BY_RELATION = NO
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
REFERENCES_RELATION = NO
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.
-# Otherwise they will link to the documentation.
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
REFERENCES_LINK_SOURCE = YES
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
USE_HTAGS = NO
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
VERBATIM_HEADERS = YES
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see:
+# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
+# performance. This can be particularly helpful with template rich C++ code for
+# which doxygen's built-in parser lacks the necessary type information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
+# tag is set to YES then doxygen will add the directory of each input to the
+# include path.
+# The default value is: YES.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_ADD_INC_PATHS = YES
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+# If clang assisted parsing is enabled you can provide the clang parser with the
+# path to the directory containing a file called compile_commands.json. This
+# file is the compilation database (see:
+# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
+# options used when the source files were built. This is equivalent to
+# specifying the -p option to a clang tool, such as clang-check. These options
+# will then be passed to the parser. Any options specified with CLANG_OPTIONS
+# will be added as well.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+
+CLANG_DATABASE_PATH =
+
#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
+# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
ALPHABETICAL_INDEX = YES
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX = 5
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
+# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
+# that should be ignored while generating the index headers. The IGNORE_PREFIX
+# tag works for classes, function and member names. The entity will be placed in
+# the alphabetical list under the first letter of the entity name that remains
+# after removing the prefix.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
-# configuration options related to the HTML output
+# Configuration options related to the HTML output
#---------------------------------------------------------------------------
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
GENERATE_HTML = YES
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = html
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FILE_EXTENSION = .html
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER =
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER =
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET =
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the stylesheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
-# The allowed range is 0 to 359.
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# Note: Since the styling of scrollbars can currently not be overruled in
+# Webkit/Chromium, the styling will be left out of the default doxygen.css if
+# one or more extra stylesheets have been specified. So if scrollbar
+# customization is desired it has to be added explicitly. For an example see the
+# documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
+# should be rendered with a dark or light theme.
+# Possible values are: LIGHT always generate light mode output, DARK always
+# generate dark mode output, AUTO_LIGHT automatically set the mode according to
+# the user preference, use light mode if no preference is set (the default),
+# AUTO_DARK automatically set the mode according to the user preference, use
+# dark mode if no preference is set and TOGGLE allow to user to switch between
+# light and dark mode via a button.
+# The default value is: AUTO_LIGHT.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE = AUTO_LIGHT
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a color-wheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_HUE = 220
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
-# grayscales only. A value of 255 will produce the most vivid colors.
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use gray-scales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_SAT = 100
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
-# and 100 does not change the gamma.
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = YES
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via JavaScript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have JavaScript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_ALIGN_MEMBERS = YES
+HTML_DYNAMIC_MENUS = YES
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_SECTIONS = NO
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see:
+# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
+# create a documentation set, doxygen will generate a Makefile in the HTML
+# output directory. Running make will produce the docset in that directory and
+# running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_DOCSET = NO
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDNAME = "Doxygen generated docs"
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
+# This tag determines the URL of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDURL =
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_BUNDLE_ID = org.doxygen.Project
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
# the documentation publisher. This should be a reverse domain-name style
# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
-# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_NAME = Publisher
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# on Windows. In the beginning of 2021 Microsoft took the original page, with
+# a.o. the download links, offline the HTML help workshop was already many years
+# in maintenance mode). You can download the HTML help workshop from the web
+# archives at Installation executable (see:
+# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
+# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_HTMLHELP = NO
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION =
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the main .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
GENERATE_CHI = NO
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_INDEX_ENCODING =
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
BINARY_TOC = NO
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
TOC_EXPAND = NO
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
-# Qt Compressed Help (.qch) of the generated HTML documentation.
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_QHP = NO
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QCH_FILE =
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_NAMESPACE = org.doxygen.Project
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_VIRTUAL_FOLDER = doc
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
-# Qt Help Project / Custom Filters</a>.
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
-# Qt Help Project / Filter Attributes</a>.
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
+# The QHG_LOCATION tag can be used to specify the location (absolute path
+# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
+# run qhelpgenerator on the generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-# will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
-# the help appears.
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_ECLIPSEHELP = NO
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
ECLIPSE_DOC_ID = org.doxygen.Project
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
DISABLE_INDEX = NO
-# This tag can be used to set the number of enum values (range [1..20])
-# that doxygen will group on one line in the generated HTML documentation.
-
-ENUM_VALUES_PER_LINE = 4
-
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine tune the look of the index (see "Fine-tuning the output"). As an
+# example, the default style sheet generated by doxygen has an example that
+# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
+# Since the tree basically has the same information as the tab index, you could
+# consider setting DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = NO
-# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list.
+# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
+# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
+# area (value NO) or if it should extend to the full height of the window (value
+# YES). Setting this to YES gives a layout similar to
+# https://docs.readthedocs.io with more room for contents, but less room for the
+# project logo, title, and description. If either GENERATE_TREEVIEW or
+# DISABLE_INDEX is set to NO, this option has no effect.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
-USE_INLINE_TREES = NO
+FULL_SIDEBAR = NO
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
TREEVIEW_WIDTH = 250
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
-# links to external symbols imported via tag files in a separate window.
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
EXT_LINKS_IN_WINDOW = NO
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
+# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
+# addresses.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+OBFUSCATE_EMAILS = YES
+
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png (the default) and svg (looks nicer but requires the
+# pdf2svg or inkscape tool).
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT = png
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_FONTSIZE = 10
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
-# in the HTML output before the changes have effect.
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
-FORMULA_TRANSPARENT = YES
+FORMULA_MACROFILE =
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box
-# for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
+# Note that the different versions of MathJax have different requirements with
+# regards to the different settings, so it is possible that also other MathJax
+# settings have to be changed when switching between the different MathJax
+# versions.
+# Possible values are: MathJax_2 and MathJax_3.
+# The default value is: MathJax_2.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_VERSION = MathJax_2
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. For more details about the output format see MathJax
+# version 2 (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
+# (see:
+# http://docs.mathjax.org/en/latest/web/components/output.html).
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility. This is the name for Mathjax version 2, for MathJax version 3
+# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
+# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
+# is the name for Mathjax version 3, for MathJax version 2 this will be
+# translated into HTML-CSS) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment. The default value is:
+# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
+# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# for MathJax version 2 (see
+# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# For example for MathJax version 3 (see
+# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
+# MATHJAX_EXTENSIONS = ams
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a PHP enabled web server instead of at the web client
-# using Javascript. Doxygen will generate the search PHP script and index
-# file to put on the web server. The advantage of the server
-# based approach is that it scales better to large projects and allows
-# full text search. The disadvances is that it is more difficult to setup
-# and does not have live searching capabilities.
+# implemented using a web server instead of a web client using JavaScript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
SERVER_BASED_SEARCH = NO
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
+# https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
+# https://xapian.org/). See the section "External Indexing and Searching" for
+# details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
+# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
GENERATE_LATEX = NO
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
+# invoked.
+#
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD = \makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
COMPACT_LATEX = NO
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, a4wide, letter, legal and
-# executive. If left blank a4wide will be used.
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
-PAPER_TYPE = a4wide
+PAPER_TYPE = a4
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES =
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
+# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
+# the generated LaTeX document. The header should contain everything until the
+# first chapter. If it is left blank doxygen will generate a standard header. It
+# is highly recommended to start with a default header using
+# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
+# and then modify the file new_header.tex. See also section "Doxygen usage" for
+# information on how to generate the default header that doxygen normally uses.
+#
+# Note: Only use a user-defined header if you know what you are doing!
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. The following
+# commands have a special meaning inside the header (and footer): For a
+# description of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
+# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
+# the generated LaTeX document. The footer should contain everything after the
+# last chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer. See also section "Doxygen
+# usage" for information on how to generate the default footer that doxygen
+# normally uses. Note: Only use a user-defined footer if you know what you are
+# doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = YES
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
-# higher quality PDF documentation.
+# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
+# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
+# files. Set this option to YES, to get a higher quality PDF documentation.
+#
+# See also section LATEX_CMD_NAME for selecting the engine.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = YES
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BATCHMODE = NO
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HIDE_INDICES = NO
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
-# such as SOURCE_BROWSER.
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
-LATEX_SOURCE_CODE = NO
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
-# configuration options related to the RTF output
+# Configuration options related to the RTF output
#---------------------------------------------------------------------------
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
GENERATE_RTF = NO
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_OUTPUT = rtf
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
COMPACT_RTF = NO
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
+# configuration file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
-# configuration options related to the man page output
+# Configuration options related to the man page output
#---------------------------------------------------------------------------
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
GENERATE_MAN = NO
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_OUTPUT = man
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_EXTENSION = .3
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_LINKS = NO
#---------------------------------------------------------------------------
-# configuration options related to the XML output
+# Configuration options related to the XML output
#---------------------------------------------------------------------------
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
GENERATE_XML = NO
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
XML_OUTPUT = xml
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_SCHEMA =
-
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_DTD =
-
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
XML_PROGRAMLISTING = YES
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
+# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
+# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
GENERATE_PERLMOD = NO
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_LATEX = NO
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.
-# This is useful
-# if you want to understand what is going on.
-# On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_PRETTY = YES
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_MAKEVAR_PREFIX =
@@ -1320,311 +2264,461 @@
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
ENABLE_PREPROCESSING = YES
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = NO
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = NO
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
+# contain include files that are not input files but should be processed by the
+# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
+# RECURSIVE has no effect here.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
INCLUDE_FILE_PATTERNS =
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED =
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition.
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED =
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all function-like macros that are alone
-# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse
-# the parser if not removed.
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
-# Configuration::additions related to external references
+# Configuration options related to external references
#---------------------------------------------------------------------------
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
-#
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
-#
# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
TAGFILES =
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE =
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
ALLEXTERNALS = NO
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
EXTERNAL_GROUPS = YES
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
-PERL_PATH = /usr/bin/perl
+EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option is superseded by the HAVE_DOT option below. This is only a
-# fallback. It is recommended to install and use dot, since it yields more
-# powerful graphs.
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
-CLASS_DIAGRAMS = YES
+DIA_PATH =
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH =
-
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
HAVE_DOT = NO
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
-# between CPU load and processing speed.
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NUM_THREADS = 0
-# By default doxygen will write a font called FreeSans.ttf to the output
-# directory and reference it in all dot files that doxygen generates. This
-# font does not include all possible unicode characters however, so when you need
-# these (or just want a differently looking font) you can specify the font name
-# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
-# which can be done by putting it in a standard location or by setting the
-# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
-# containing the font.
+# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
+# subgraphs. When you want a differently looking font in the dot files that
+# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
+# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
+# Edge and Graph Attributes specification</a> You need to make sure dot is able
+# to find the font, which can be done by putting it in a standard location or by
+# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font. Default graphviz fontsize is 14.
+# The default value is: fontname=Helvetica,fontsize=10.
+# This tag requires that the tag HAVE_DOT is set to YES.
-DOT_FONTNAME = FreeSans.ttf
+DOT_COMMON_ATTR = "fontname=FreeSans.ttf,fontsize=10"
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
+# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
+# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
+# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
+# arrows shapes.</a>
+# The default value is: labelfontname=Helvetica,labelfontsize=10.
+# This tag requires that the tag HAVE_DOT is set to YES.
-DOT_FONTSIZE = 10
+DOT_EDGE_ATTR = "labelfontname=FreeSans.ttf,labelfontsize=10"
-# By default doxygen will tell dot to use the output directory to look for the
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a
-# different font using DOT_FONTNAME you can set the path where dot
-# can find it using this tag.
+# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
+# around nodes set 'shape=plain' or 'shape=plaintext' <a
+# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
+# The default value is: shape=box,height=0.2,width=0.4.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
+
+# You can set the path where dot can find font specified with fontname in
+# DOT_COMMON_ATTR and others dot attributes.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
+# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
+# graph for each documented class showing the direct and indirect inheritance
+# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
+# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
+# to TEXT the direct and indirect inheritance relations will be shown as texts /
+# links.
+# Possible values are: NO, YES, TEXT and GRAPH.
+# The default value is: YES.
CLASS_GRAPH = YES
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies. See also the chapter Grouping
+# in the manual.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
GROUP_GRAPHS = YES
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
UML_LOOK = NO
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
+# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
+# tag is set to YES, doxygen will add type and arguments for attributes and
+# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
+# will not generate fields with class member information in the UML graphs. The
+# class diagrams will look similar to the default class diagrams but using UML
+# notation for the relationships.
+# Possible values are: NO, YES and NONE.
+# The default value is: NO.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+DOT_UML_DETAILS = NO
+
+# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
+# to display on a single line. If the actual line length exceeds this threshold
+# significantly it will wrapped across multiple lines. Some heuristics are apply
+# to avoid ugly line breaks.
+# Minimum value: 0, maximum value: 1000, default value: 17.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_WRAP_THRESHOLD = 17
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
TEMPLATE_RELATIONS = NO
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDE_GRAPH = YES
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDED_BY_GRAPH = YES
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
CALL_GRAPH = NO
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
CALLER_GRAPH = NO
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will graphical hierarchy of all classes instead of a textual one.
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
GRAPHICAL_HIERARCHY = YES
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
DIRECTORY_GRAPH = YES
+# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels
+# of child directories generated in directory dependency graphs by dot.
+# Minimum value: 1, maximum value: 25, default value: 1.
+# This tag requires that the tag DIRECTORY_GRAPH is set to YES.
+
+DIR_GRAPH_MAX_DEPTH = 1
+
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, jpg, or gif
-# If left blank png will be used.
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_IMAGE_FORMAT = png
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
DOTFILE_DIRS =
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file or to the filename of jar file
+# to be used. If left blank, it is assumed PlantUML is not used or called during
+# a preprocessing step. Doxygen will generate a warning when it encounters a
+# \startuml command in this case and will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_GRAPH_MAX_NODES = 50
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
MAX_DOT_GRAPH_DEPTH = 0
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
-
-DOT_TRANSPARENT = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_MULTI_TARGETS = YES
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
+# graphical representation for inheritance and collaboration diagrams is used.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
GENERATE_LEGEND = YES
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
+# files that are used to generate the various graphs.
+#
+# Note: This setting is not only used for dot files but also for msc temporary
+# files.
+# The default value is: YES.
DOT_CLEANUP = YES
diff --git a/src/hidapi/doxygen/main_page.md b/src/hidapi/doxygen/main_page.md
new file mode 100644
index 0000000..ff11e9a
--- /dev/null
+++ b/src/hidapi/doxygen/main_page.md
@@ -0,0 +1,13 @@
+# HIDAPI Doxygen output
+
+This site is dedicated to hosting an [API reference for the HIDAPI library](#API).
+
+For general information, see the [source repository](https://github.com/libusb/hidapi#readme).
+
+There are also build instructions hosted on github:
+
+- [Building from source](https://github.com/libusb/hidapi/blob/master/BUILD.md)
+- [Using CMake](https://github.com/libusb/hidapi/blob/master/BUILD.cmake.md)
+- [Using Autotools (deprecated)](https://github.com/libusb/hidapi/blob/master/BUILD.autotools.md)
+
+\example test.c contains a basic example usage of the HIDAPI library.
diff --git a/src/hidapi/hidapi/hidapi.h b/src/hidapi/hidapi/hidapi.h
index 9fb8da4..744ceb0 100644
--- a/src/hidapi/hidapi/hidapi.h
+++ b/src/hidapi/hidapi/hidapi.h
@@ -5,9 +5,9 @@
Alan Ott
Signal 11 Software
- 8/22/2009
+ libusb/hidapi Team
- Copyright 2009, All Rights Reserved.
+ Copyright 2023, All Rights Reserved.
At the discretion of the user of this library,
this software may be licensed under the terms of the
@@ -29,36 +29,123 @@
#include <wchar.h>
-#ifdef SDL_hidapi_h_
-#define SDL_HIDAPI_IMPLEMENTATION
-#define hid_device_info SDL_hid_device_info
-#endif
-
-#if defined(_WIN32) && !defined(NAMESPACE) && !defined(SDL_HIDAPI_IMPLEMENTATION) /* SDL: don't export hidapi syms */
+/* #480: this is to be refactored properly for v1.0 */
+#ifdef _WIN32
+ #ifndef HID_API_NO_EXPORT_DEFINE
#define HID_API_EXPORT __declspec(dllexport)
- #define HID_API_CALL
-#else
+ #endif
+#endif
#ifndef HID_API_EXPORT
- #define HID_API_EXPORT /**< API export macro */
+ #define HID_API_EXPORT /**< API export macro */
#endif
-#ifndef HID_API_CALL
- #define HID_API_CALL /**< API call macro */
-#endif
-#endif
+/* To be removed in v1.0 */
+#define HID_API_CALL /**< API call macro */
#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
-#if defined(__cplusplus) && !defined(NAMESPACE)
+/** @brief Static/compile-time major version of the library.
+
+ @ingroup API
+*/
+#define HID_API_VERSION_MAJOR 0
+/** @brief Static/compile-time minor version of the library.
+
+ @ingroup API
+*/
+#define HID_API_VERSION_MINOR 14
+/** @brief Static/compile-time patch version of the library.
+
+ @ingroup API
+*/
+#define HID_API_VERSION_PATCH 0
+
+/* Helper macros */
+#define HID_API_AS_STR_IMPL(x) #x
+#define HID_API_AS_STR(x) HID_API_AS_STR_IMPL(x)
+#define HID_API_TO_VERSION_STR(v1, v2, v3) HID_API_AS_STR(v1.v2.v3)
+
+/** @brief Coverts a version as Major/Minor/Patch into a number:
+ <8 bit major><16 bit minor><8 bit patch>.
+
+ This macro was added in version 0.12.0.
+
+ Convenient function to be used for compile-time checks, like:
+ @code{.c}
+ #if HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+ @endcode
+
+ @ingroup API
+*/
+#define HID_API_MAKE_VERSION(mj, mn, p) (((mj) << 24) | ((mn) << 8) | (p))
+
+/** @brief Static/compile-time version of the library.
+
+ This macro was added in version 0.12.0.
+
+ @see @ref HID_API_MAKE_VERSION.
+
+ @ingroup API
+*/
+#define HID_API_VERSION HID_API_MAKE_VERSION(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH)
+
+/** @brief Static/compile-time string version of the library.
+
+ @ingroup API
+*/
+#define HID_API_VERSION_STR HID_API_TO_VERSION_STR(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH)
+
+/** @brief Maximum expected HID Report descriptor size in bytes.
+
+ Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
+
+ @ingroup API
+*/
+#define HID_API_MAX_REPORT_DESCRIPTOR_SIZE 4096
+
+#ifdef __cplusplus
extern "C" {
#endif
-#ifdef NAMESPACE
-namespace NAMESPACE {
-#endif
+ /** A structure to hold the version numbers. */
+ struct hid_api_version {
+ int major; /**< major version number */
+ int minor; /**< minor version number */
+ int patch; /**< patch version number */
+ };
struct hid_device_;
typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
-#ifndef SDL_HIDAPI_IMPLEMENTATION
+ /** @brief HID underlying bus types.
+
+ @ingroup API
+ */
+ typedef enum {
+ /** Unknown bus type */
+ HID_API_BUS_UNKNOWN = 0x00,
+
+ /** USB bus
+ Specifications:
+ https://usb.org/hid */
+ HID_API_BUS_USB = 0x01,
+
+ /** Bluetooth or Bluetooth LE bus
+ Specifications:
+ https://www.bluetooth.com/specifications/specs/human-interface-device-profile-1-1-1/
+ https://www.bluetooth.com/specifications/specs/hid-service-1-0/
+ https://www.bluetooth.com/specifications/specs/hid-over-gatt-profile-1-0/ */
+ HID_API_BUS_BLUETOOTH = 0x02,
+
+ /** I2C bus
+ Specifications:
+ https://docs.microsoft.com/previous-versions/windows/hardware/design/dn642101(v=vs.85) */
+ HID_API_BUS_I2C = 0x03,
+
+ /** SPI bus
+ Specifications:
+ https://www.microsoft.com/download/details.aspx?id=103325 */
+ HID_API_BUS_SPI = 0x04,
+ } hid_bus_type;
+
/** hidapi info structure */
struct hid_device_info {
/** Platform-specific device path */
@@ -77,29 +164,27 @@
/** Product string */
wchar_t *product_string;
/** Usage Page for this Device/Interface
- (Windows/Mac only). */
+ (Windows/Mac/hidraw only) */
unsigned short usage_page;
/** Usage for this Device/Interface
- (Windows/Mac only).*/
+ (Windows/Mac/hidraw only) */
unsigned short usage;
/** The USB interface which this logical device
represents.
- * Valid on both Linux implementations in all cases.
- * Valid on the Windows implementation only if the device
- contains more than one interface. */
+ Valid only if the device is a USB HID device.
+ Set to -1 in all other cases.
+ */
int interface_number;
- /** Additional information about the USB interface.
- Valid on libusb and Android implementations. */
- int interface_class;
- int interface_subclass;
- int interface_protocol;
-
/** Pointer to the next device */
struct hid_device_info *next;
+
+ /** Underlying bus type
+ Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
+ */
+ hid_bus_type bus_type;
};
-#endif /* !SDL_HIDAPI_IMPLEMENTATION */
/** @brief Initialize the HIDAPI library.
@@ -115,6 +200,7 @@
@returns
This function returns 0 on success and -1 on error.
+ Call hid_error(NULL) to get the failure reason.
*/
int HID_API_EXPORT HID_API_CALL hid_init(void);
@@ -126,7 +212,7 @@
@ingroup API
- @returns
+ @returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_exit(void);
@@ -146,21 +232,25 @@
@param product_id The Product ID (PID) of the types of
device to open.
- @returns
- This function returns a pointer to a linked list of type
- struct #hid_device_info, containing information about the HID devices
- attached to the system, or NULL in the case of failure. Free
- this linked list by calling hid_free_enumeration().
+ @returns
+ This function returns a pointer to a linked list of type
+ struct #hid_device_info, containing information about the HID devices
+ attached to the system,
+ or NULL in the case of failure or if no HID devices present in the system.
+ Call hid_error(NULL) to get the failure reason.
+
+ @note The returned value by this function must to be freed by calling hid_free_enumeration(),
+ when not needed anymore.
*/
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
/** @brief Free an enumeration Linked List
- This function frees a linked list created by hid_enumerate().
+ This function frees a linked list created by hid_enumerate().
@ingroup API
- @param devs Pointer to a list of struct_device returned from
- hid_enumerate().
+ @param devs Pointer to a list of struct_device returned from
+ hid_enumerate().
*/
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
@@ -174,11 +264,15 @@
@param vendor_id The Vendor ID (VID) of the device to open.
@param product_id The Product ID (PID) of the device to open.
@param serial_number The Serial Number of the device to open
- (Optionally NULL).
+ (Optionally NULL).
@returns
This function returns a pointer to a #hid_device object on
success or NULL on failure.
+ Call hid_error(NULL) to get the failure reason.
+
+ @note The returned object must be freed by calling hid_close(),
+ when not needed anymore.
*/
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
@@ -189,13 +283,17 @@
Linux).
@ingroup API
- @param path The path name of the device to open
+ @param path The path name of the device to open
@returns
This function returns a pointer to a #hid_device object on
success or NULL on failure.
+ Call hid_error(NULL) to get the failure reason.
+
+ @note The returned object must be freed by calling hid_close(),
+ when not needed anymore.
*/
- HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bExclusive /* = false */);
+ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);
/** @brief Write an Output report to a HID device.
@@ -222,6 +320,7 @@
@returns
This function returns the actual number of bytes written and
-1 on error.
+ Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length);
@@ -241,7 +340,9 @@
@returns
This function returns the actual number of bytes read and
- -1 on error. If no packet was available to be read within
+ -1 on error.
+ Call hid_error(dev) to get the failure reason.
+ If no packet was available to be read within
the timeout period, this function returns 0.
*/
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
@@ -249,7 +350,7 @@
/** @brief Read an Input report from a HID device.
Input reports are returned
- to the host through the INTERRUPT IN endpoint. The first byte will
+ to the host through the INTERRUPT IN endpoint. The first byte will
contain the Report number if the device uses numbered reports.
@ingroup API
@@ -261,7 +362,9 @@
@returns
This function returns the actual number of bytes read and
- -1 on error. If no packet was available to be read and
+ -1 on error.
+ Call hid_error(dev) to get the failure reason.
+ If no packet was available to be read and
the handle is in non-blocking mode, this function returns 0.
*/
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length);
@@ -283,6 +386,7 @@
@returns
This function returns 0 on success and -1 on error.
+ Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock);
@@ -311,6 +415,7 @@
@returns
This function returns the actual number of bytes written and
-1 on error.
+ Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length);
@@ -336,9 +441,38 @@
This function returns the number of bytes read plus
one for the report ID (which is still in the first
byte), or -1 on error.
+ Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length);
+ /** @brief Get a input report from a HID device.
+
+ Since version 0.10.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 10, 0)
+
+ Set the first byte of @p data[] to the Report ID of the
+ report to be read. Make sure to allow space for this
+ extra byte in @p data[]. Upon return, the first byte will
+ still contain the Report ID, and the report data will
+ start in data[1].
+
+ @ingroup API
+ @param dev A device handle returned from hid_open().
+ @param data A buffer to put the read data into, including
+ the Report ID. Set the first byte of @p data[] to the
+ Report ID of the report to be read, or set it to zero
+ if your device does not use numbered reports.
+ @param length The number of bytes to read, including an
+ extra byte for the report ID. The buffer can be longer
+ than the actual report.
+
+ @returns
+ This function returns the number of bytes read plus
+ one for the report ID (which is still in the first
+ byte), or -1 on error.
+ Call hid_error(dev) to get the failure reason.
+ */
+ int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length);
+
/** @brief Close a HID device.
@ingroup API
@@ -355,6 +489,7 @@
@returns
This function returns 0 on success and -1 on error.
+ Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen);
@@ -367,6 +502,7 @@
@returns
This function returns 0 on success and -1 on error.
+ Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen);
@@ -379,9 +515,27 @@
@returns
This function returns 0 on success and -1 on error.
+ Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen);
+ /** @brief Get The struct #hid_device_info from a HID device.
+
+ Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
+
+ @ingroup API
+ @param dev A device handle returned from hid_open().
+
+ @returns
+ This function returns a pointer to the struct #hid_device_info
+ for this hid_device, or NULL in the case of failure.
+ Call hid_error(dev) to get the failure reason.
+ This struct is valid until the device is closed with hid_close().
+
+ @note The returned object is owned by the @p dev, and SHOULD NOT be freed by the user.
+ */
+ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_get_device_info(hid_device *dev);
+
/** @brief Get a string from a HID device, based on its string index.
@ingroup API
@@ -392,30 +546,79 @@
@returns
This function returns 0 on success and -1 on error.
+ Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen);
- /** @brief Get a string describing the last error which occurred.
+ /** @brief Get a report descriptor from a HID device.
+
+ Since version 0.14.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 14, 0)
+
+ User has to provide a preallocated buffer where descriptor will be copied to.
+ The recommended size for preallocated buffer is @ref HID_API_MAX_REPORT_DESCRIPTOR_SIZE bytes.
@ingroup API
@param dev A device handle returned from hid_open().
+ @param buf The buffer to copy descriptor into.
+ @param buf_size The size of the buffer in bytes.
@returns
- This function returns a string containing the last error
- which occurred or NULL if none has occurred.
+ This function returns non-negative number of bytes actually copied, or -1 on error.
+ */
+ int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size);
+
+ /** @brief Get a string describing the last error which occurred.
+
+ This function is intended for logging/debugging purposes.
+
+ This function guarantees to never return NULL.
+ If there was no error in the last function call -
+ the returned string clearly indicates that.
+
+ Any HIDAPI function that can explicitly indicate an execution failure
+ (e.g. by an error code, or by returning NULL) - may set the error string,
+ to be returned by this function.
+
+ Strings returned from hid_error() must not be freed by the user,
+ i.e. owned by HIDAPI library.
+ Device-specific error string may remain allocated at most until hid_close() is called.
+ Global error string may remain allocated at most until hid_exit() is called.
+
+ @ingroup API
+ @param dev A device handle returned from hid_open(),
+ or NULL to get the last non-device-specific error
+ (e.g. for errors in hid_open() or hid_enumerate()).
+
+ @returns
+ A string describing the last error (if any).
*/
HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev);
-#if defined(__IOS__) || defined(__TVOS__)
- HID_API_EXPORT void HID_API_CALL hid_ble_scan(int active);
-#endif
+ /** @brief Get a runtime version of the library.
-#if defined(__cplusplus) && !defined(NAMESPACE)
-}
-#endif
-#ifdef NAMESPACE
+ This function is thread-safe.
+
+ @ingroup API
+
+ @returns
+ Pointer to statically allocated struct, that contains version.
+ */
+ HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void);
+
+
+ /** @brief Get a runtime version string of the library.
+
+ This function is thread-safe.
+
+ @ingroup API
+
+ @returns
+ Pointer to statically allocated string, that contains version string.
+ */
+ HID_API_EXPORT const char* HID_API_CALL hid_version_str(void);
+
+#ifdef __cplusplus
}
#endif
#endif
-
diff --git a/src/hidapi/hidtest/.gitignore b/src/hidapi/hidtest/.gitignore
new file mode 100644
index 0000000..a9ce7a2
--- /dev/null
+++ b/src/hidapi/hidtest/.gitignore
@@ -0,0 +1,17 @@
+Debug
+Release
+*.exp
+*.ilk
+*.lib
+*.suo
+*.vcproj.*
+*.ncb
+*.suo
+*.dll
+*.pdb
+*.o
+.deps/
+.libs/
+hidtest-hidraw
+hidtest-libusb
+hidtest
diff --git a/src/hidapi/hidtest/CMakeLists.txt b/src/hidapi/hidtest/CMakeLists.txt
new file mode 100644
index 0000000..701a4fb
--- /dev/null
+++ b/src/hidapi/hidtest/CMakeLists.txt
@@ -0,0 +1,40 @@
+cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR)
+project(hidtest C)
+
+if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
+ # hidtest is build as a standalone project
+
+ if(POLICY CMP0074)
+ # allow using hidapi_ROOT if CMake supports it
+ cmake_policy(SET CMP0074 NEW)
+ endif()
+
+ find_package(hidapi 0.12 REQUIRED)
+ message(STATUS "Using HIDAPI: ${hidapi_VERSION}")
+else()
+ # hidtest is built as part of the main HIDAPI build
+ message(STATUS "Building hidtest")
+endif()
+
+set(HIDAPI_HIDTEST_TARGETS)
+if(NOT WIN32 AND NOT APPLE AND CMAKE_SYSTEM_NAME MATCHES "Linux")
+ if(TARGET hidapi::hidraw)
+ add_executable(hidtest_hidraw test.c)
+ target_link_libraries(hidtest_hidraw hidapi::hidraw)
+ list(APPEND HIDAPI_HIDTEST_TARGETS hidtest_hidraw)
+ endif()
+ if(TARGET hidapi::libusb)
+ add_executable(hidtest_libusb test.c)
+ target_compile_definitions(hidtest_libusb PRIVATE USING_HIDAPI_LIBUSB)
+ target_link_libraries(hidtest_libusb hidapi::libusb)
+ list(APPEND HIDAPI_HIDTEST_TARGETS hidtest_libusb)
+ endif()
+else()
+ add_executable(hidtest test.c)
+ target_link_libraries(hidtest hidapi::hidapi)
+ list(APPEND HIDAPI_HIDTEST_TARGETS hidtest)
+endif()
+
+install(TARGETS ${HIDAPI_HIDTEST_TARGETS}
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+)
diff --git a/src/hidapi/hidtest/Makefile.am b/src/hidapi/hidtest/Makefile.am
index d278644..b601307 100644
--- a/src/hidapi/hidtest/Makefile.am
+++ b/src/hidapi/hidtest/Makefile.am
@@ -4,17 +4,25 @@
if OS_LINUX
noinst_PROGRAMS = hidtest-libusb hidtest-hidraw
-hidtest_hidraw_SOURCES = hidtest.cpp
+hidtest_hidraw_SOURCES = test.c
hidtest_hidraw_LDADD = $(top_builddir)/linux/libhidapi-hidraw.la
-hidtest_libusb_SOURCES = hidtest.cpp
+hidtest_libusb_SOURCES = test.c
hidtest_libusb_LDADD = $(top_builddir)/libusb/libhidapi-libusb.la
else
# Other OS's
noinst_PROGRAMS = hidtest
-hidtest_SOURCES = hidtest.cpp
+hidtest_SOURCES = test.c
hidtest_LDADD = $(top_builddir)/$(backend)/libhidapi.la
endif
+
+if OS_DARWIN
+AM_CPPFLAGS += -I$(top_srcdir)/mac/
+endif
+
+if OS_WINDOWS
+AM_CPPFLAGS += -I$(top_srcdir)/windows/
+endif
diff --git a/src/hidapi/hidtest/hidtest.cpp b/src/hidapi/hidtest/hidtest.cpp
deleted file mode 100644
index 94f0a5c..0000000
--- a/src/hidapi/hidtest/hidtest.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
-/*******************************************************
- Windows HID simplification
-
- Alan Ott
- Signal 11 Software
-
- 8/22/2009
-
- Copyright 2009
-
- This contents of this file may be used by anyone
- for any reason without any conditions and may be
- used as a starting point for your own applications
- which use HIDAPI.
-********************************************************/
-
-#include <stdio.h>
-#include <wchar.h>
-#include <string.h>
-#include <stdlib.h>
-#include "hidapi.h"
-
-// Headers needed for sleeping.
-#ifdef _WIN32
- #include <windows.h>
-#else
- #include <unistd.h>
-#endif
-
-int main(int argc, char* argv[])
-{
- int res;
- unsigned char buf[256];
- #define MAX_STR 255
- wchar_t wstr[MAX_STR];
- hid_device *handle;
- int i;
-
-#ifdef WIN32
- UNREFERENCED_PARAMETER(argc);
- UNREFERENCED_PARAMETER(argv);
-#endif
-
- struct hid_device_info *devs, *cur_dev;
-
- if (hid_init())
- return -1;
-
- devs = hid_enumerate(0x0, 0x0);
- cur_dev = devs;
- while (cur_dev) {
- printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
- printf("\n");
- printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string);
- printf(" Product: %ls\n", cur_dev->product_string);
- printf(" Release: %hx\n", cur_dev->release_number);
- printf(" Interface: %d\n", cur_dev->interface_number);
- printf("\n");
- cur_dev = cur_dev->next;
- }
- hid_free_enumeration(devs);
-
- // Set up the command buffer.
- memset(buf,0x00,sizeof(buf));
- buf[0] = 0x01;
- buf[1] = 0x81;
-
-
- // Open the device using the VID, PID,
- // and optionally the Serial number.
- ////handle = hid_open(0x4d8, 0x3f, L"12345");
- handle = hid_open(0x4d8, 0x3f, NULL);
- if (!handle) {
- printf("unable to open device\n");
- return 1;
- }
-
- // Read the Manufacturer String
- wstr[0] = 0x0000;
- res = hid_get_manufacturer_string(handle, wstr, MAX_STR);
- if (res < 0)
- printf("Unable to read manufacturer string\n");
- printf("Manufacturer String: %ls\n", wstr);
-
- // Read the Product String
- wstr[0] = 0x0000;
- res = hid_get_product_string(handle, wstr, MAX_STR);
- if (res < 0)
- printf("Unable to read product string\n");
- printf("Product String: %ls\n", wstr);
-
- // Read the Serial Number String
- wstr[0] = 0x0000;
- res = hid_get_serial_number_string(handle, wstr, MAX_STR);
- if (res < 0)
- printf("Unable to read serial number string\n");
- printf("Serial Number String: (%d) %ls", wstr[0], wstr);
- printf("\n");
-
- // Read Indexed String 1
- wstr[0] = 0x0000;
- res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
- if (res < 0)
- printf("Unable to read indexed string 1\n");
- printf("Indexed String 1: %ls\n", wstr);
-
- // Set the hid_read() function to be non-blocking.
- hid_set_nonblocking(handle, 1);
-
- // Try to read from the device. There shoud be no
- // data here, but execution should not block.
- res = hid_read(handle, buf, 17);
-
- // Send a Feature Report to the device
- buf[0] = 0x2;
- buf[1] = 0xa0;
- buf[2] = 0x0a;
- buf[3] = 0x00;
- buf[4] = 0x00;
- res = hid_send_feature_report(handle, buf, 17);
- if (res < 0) {
- printf("Unable to send a feature report.\n");
- }
-
- memset(buf,0,sizeof(buf));
-
- // Read a Feature Report from the device
- buf[0] = 0x2;
- res = hid_get_feature_report(handle, buf, sizeof(buf));
- if (res < 0) {
- printf("Unable to get a feature report.\n");
- printf("%ls", hid_error(handle));
- }
- else {
- // Print out the returned buffer.
- printf("Feature Report\n ");
- for (i = 0; i < res; i++)
- printf("%02hhx ", buf[i]);
- printf("\n");
- }
-
- memset(buf,0,sizeof(buf));
-
- // Toggle LED (cmd 0x80). The first byte is the report number (0x1).
- buf[0] = 0x1;
- buf[1] = 0x80;
- res = hid_write(handle, buf, 17);
- if (res < 0) {
- printf("Unable to write()\n");
- printf("Error: %ls\n", hid_error(handle));
- }
-
-
- // Request state (cmd 0x81). The first byte is the report number (0x1).
- buf[0] = 0x1;
- buf[1] = 0x81;
- hid_write(handle, buf, 17);
- if (res < 0)
- printf("Unable to write() (2)\n");
-
- // Read requested state. hid_read() has been set to be
- // non-blocking by the call to hid_set_nonblocking() above.
- // This loop demonstrates the non-blocking nature of hid_read().
- res = 0;
- while (res == 0) {
- res = hid_read(handle, buf, sizeof(buf));
- if (res == 0)
- printf("waiting...\n");
- if (res < 0)
- printf("Unable to read()\n");
- #ifdef WIN32
- Sleep(500);
- #else
- usleep(500*1000);
- #endif
- }
-
- printf("Data read:\n ");
- // Print out the returned buffer.
- for (i = 0; i < res; i++)
- printf("%02hhx ", buf[i]);
- printf("\n");
-
- hid_close(handle);
-
- /* Free static HIDAPI objects. */
- hid_exit();
-
-#ifdef WIN32
- system("pause");
-#endif
-
- return 0;
-}
diff --git a/src/hidapi/hidtest/test.c b/src/hidapi/hidtest/test.c
new file mode 100644
index 0000000..94bbf37
--- /dev/null
+++ b/src/hidapi/hidtest/test.c
@@ -0,0 +1,316 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ Alan Ott
+ Signal 11 Software
+
+ libusb/hidapi Team
+
+ Copyright 2022.
+
+ This contents of this file may be used by anyone
+ for any reason without any conditions and may be
+ used as a starting point for your own applications
+ which use HIDAPI.
+********************************************************/
+
+#include <stdio.h>
+#include <wchar.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <hidapi.h>
+
+// Headers needed for sleeping.
+#ifdef _WIN32
+ #include <windows.h>
+#else
+ #include <unistd.h>
+#endif
+
+// Fallback/example
+#ifndef HID_API_MAKE_VERSION
+#define HID_API_MAKE_VERSION(mj, mn, p) (((mj) << 24) | ((mn) << 8) | (p))
+#endif
+#ifndef HID_API_VERSION
+#define HID_API_VERSION HID_API_MAKE_VERSION(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH)
+#endif
+
+//
+// Sample using platform-specific headers
+#if defined(__APPLE__) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+#include <hidapi_darwin.h>
+#endif
+
+#if defined(_WIN32) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+#include <hidapi_winapi.h>
+#endif
+
+#if defined(USING_HIDAPI_LIBUSB) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+#include <hidapi_libusb.h>
+#endif
+//
+
+const char *hid_bus_name(hid_bus_type bus_type) {
+ static const char *const HidBusTypeName[] = {
+ "Unknown",
+ "USB",
+ "Bluetooth",
+ "I2C",
+ "SPI",
+ };
+
+ if ((int)bus_type < 0)
+ bus_type = HID_API_BUS_UNKNOWN;
+ if ((int)bus_type >= (int)(sizeof(HidBusTypeName) / sizeof(HidBusTypeName[0])))
+ bus_type = HID_API_BUS_UNKNOWN;
+
+ return HidBusTypeName[bus_type];
+}
+
+void print_device(struct hid_device_info *cur_dev) {
+ printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
+ printf("\n");
+ printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string);
+ printf(" Product: %ls\n", cur_dev->product_string);
+ printf(" Release: %hx\n", cur_dev->release_number);
+ printf(" Interface: %d\n", cur_dev->interface_number);
+ printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page);
+ printf(" Bus type: %d (%s)\n", cur_dev->bus_type, hid_bus_name(cur_dev->bus_type));
+ printf("\n");
+}
+
+void print_hid_report_descriptor_from_device(hid_device *device) {
+ unsigned char descriptor[HID_API_MAX_REPORT_DESCRIPTOR_SIZE];
+ int res = 0;
+
+ printf(" Report Descriptor: ");
+ res = hid_get_report_descriptor(device, descriptor, sizeof(descriptor));
+ if (res < 0) {
+ printf("error getting: %ls", hid_error(device));
+ }
+ else {
+ printf("(%d bytes)", res);
+ }
+ for (int i = 0; i < res; i++) {
+ if (i % 10 == 0) {
+ printf("\n");
+ }
+ printf("0x%02x, ", descriptor[i]);
+ }
+ printf("\n");
+}
+
+void print_hid_report_descriptor_from_path(const char *path) {
+ hid_device *device = hid_open_path(path);
+ if (device) {
+ print_hid_report_descriptor_from_device(device);
+ hid_close(device);
+ }
+ else {
+ printf(" Report Descriptor: Unable to open device by path\n");
+ }
+}
+
+void print_devices(struct hid_device_info *cur_dev) {
+ for (; cur_dev; cur_dev = cur_dev->next) {
+ print_device(cur_dev);
+ }
+}
+
+void print_devices_with_descriptor(struct hid_device_info *cur_dev) {
+ for (; cur_dev; cur_dev = cur_dev->next) {
+ print_device(cur_dev);
+ print_hid_report_descriptor_from_path(cur_dev->path);
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ (void)argc;
+ (void)argv;
+
+ int res;
+ unsigned char buf[256];
+ #define MAX_STR 255
+ wchar_t wstr[MAX_STR];
+ hid_device *handle;
+ int i;
+
+ struct hid_device_info *devs;
+
+ printf("hidapi test/example tool. Compiled with hidapi version %s, runtime version %s.\n", HID_API_VERSION_STR, hid_version_str());
+ if (HID_API_VERSION == HID_API_MAKE_VERSION(hid_version()->major, hid_version()->minor, hid_version()->patch)) {
+ printf("Compile-time version matches runtime version of hidapi.\n\n");
+ }
+ else {
+ printf("Compile-time version is different than runtime version of hidapi.\n]n");
+ }
+
+ if (hid_init())
+ return -1;
+
+#if defined(__APPLE__) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+ // To work properly needs to be called before hid_open/hid_open_path after hid_init.
+ // Best/recommended option - call it right after hid_init.
+ hid_darwin_set_open_exclusive(0);
+#endif
+
+ devs = hid_enumerate(0x0, 0x0);
+ print_devices_with_descriptor(devs);
+ hid_free_enumeration(devs);
+
+ // Set up the command buffer.
+ memset(buf,0x00,sizeof(buf));
+ buf[0] = 0x01;
+ buf[1] = 0x81;
+
+
+ // Open the device using the VID, PID,
+ // and optionally the Serial number.
+ ////handle = hid_open(0x4d8, 0x3f, L"12345");
+ handle = hid_open(0x4d8, 0x3f, NULL);
+ if (!handle) {
+ printf("unable to open device\n");
+ hid_exit();
+ return 1;
+ }
+
+ // Read the Manufacturer String
+ wstr[0] = 0x0000;
+ res = hid_get_manufacturer_string(handle, wstr, MAX_STR);
+ if (res < 0)
+ printf("Unable to read manufacturer string\n");
+ printf("Manufacturer String: %ls\n", wstr);
+
+ // Read the Product String
+ wstr[0] = 0x0000;
+ res = hid_get_product_string(handle, wstr, MAX_STR);
+ if (res < 0)
+ printf("Unable to read product string\n");
+ printf("Product String: %ls\n", wstr);
+
+ // Read the Serial Number String
+ wstr[0] = 0x0000;
+ res = hid_get_serial_number_string(handle, wstr, MAX_STR);
+ if (res < 0)
+ printf("Unable to read serial number string\n");
+ printf("Serial Number String: (%d) %ls\n", wstr[0], wstr);
+
+ print_hid_report_descriptor_from_device(handle);
+
+ struct hid_device_info* info = hid_get_device_info(handle);
+ if (info == NULL) {
+ printf("Unable to get device info\n");
+ } else {
+ print_devices(info);
+ }
+
+ // Read Indexed String 1
+ wstr[0] = 0x0000;
+ res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
+ if (res < 0)
+ printf("Unable to read indexed string 1\n");
+ printf("Indexed String 1: %ls\n", wstr);
+
+ // Set the hid_read() function to be non-blocking.
+ hid_set_nonblocking(handle, 1);
+
+ // Try to read from the device. There should be no
+ // data here, but execution should not block.
+ res = hid_read(handle, buf, 17);
+
+ // Send a Feature Report to the device
+ buf[0] = 0x2;
+ buf[1] = 0xa0;
+ buf[2] = 0x0a;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+ res = hid_send_feature_report(handle, buf, 17);
+ if (res < 0) {
+ printf("Unable to send a feature report.\n");
+ }
+
+ memset(buf,0,sizeof(buf));
+
+ // Read a Feature Report from the device
+ buf[0] = 0x2;
+ res = hid_get_feature_report(handle, buf, sizeof(buf));
+ if (res < 0) {
+ printf("Unable to get a feature report: %ls\n", hid_error(handle));
+ }
+ else {
+ // Print out the returned buffer.
+ printf("Feature Report\n ");
+ for (i = 0; i < res; i++)
+ printf("%02x ", (unsigned int) buf[i]);
+ printf("\n");
+ }
+
+ memset(buf,0,sizeof(buf));
+
+ // Toggle LED (cmd 0x80). The first byte is the report number (0x1).
+ buf[0] = 0x1;
+ buf[1] = 0x80;
+ res = hid_write(handle, buf, 17);
+ if (res < 0) {
+ printf("Unable to write(): %ls\n", hid_error(handle));
+ }
+
+
+ // Request state (cmd 0x81). The first byte is the report number (0x1).
+ buf[0] = 0x1;
+ buf[1] = 0x81;
+ hid_write(handle, buf, 17);
+ if (res < 0) {
+ printf("Unable to write()/2: %ls\n", hid_error(handle));
+ }
+
+ // Read requested state. hid_read() has been set to be
+ // non-blocking by the call to hid_set_nonblocking() above.
+ // This loop demonstrates the non-blocking nature of hid_read().
+ res = 0;
+ i = 0;
+ while (res == 0) {
+ res = hid_read(handle, buf, sizeof(buf));
+ if (res == 0) {
+ printf("waiting...\n");
+ }
+ if (res < 0) {
+ printf("Unable to read(): %ls\n", hid_error(handle));
+ break;
+ }
+
+ i++;
+ if (i >= 10) { /* 10 tries by 500 ms - 5 seconds of waiting*/
+ printf("read() timeout\n");
+ break;
+ }
+
+#ifdef _WIN32
+ Sleep(500);
+#else
+ usleep(500*1000);
+#endif
+ }
+
+ if (res > 0) {
+ printf("Data read:\n ");
+ // Print out the returned buffer.
+ for (i = 0; i < res; i++)
+ printf("%02x ", (unsigned int) buf[i]);
+ printf("\n");
+ }
+
+ hid_close(handle);
+
+ /* Free static HIDAPI objects. */
+ hid_exit();
+
+#ifdef _WIN32
+ system("pause");
+#endif
+
+ return 0;
+}
diff --git a/src/hidapi/ios/Makefile-manual b/src/hidapi/ios/Makefile-manual
deleted file mode 100644
index 939a077..0000000
--- a/src/hidapi/ios/Makefile-manual
+++ /dev/null
@@ -1,32 +0,0 @@
-###########################################
-# Simple Makefile for HIDAPI test program
-#
-# Alan Ott
-# Signal 11 Software
-# 2010-07-03
-###########################################
-
-all: hidtest
-
-CC=gcc
-CXX=g++
-COBJS=hid.o
-CPPOBJS=../hidtest/hidtest.o
-OBJS=$(COBJS) $(CPPOBJS)
-CFLAGS+=-I../hidapi -Wall -g -c
-LIBS=-framework CoreBluetooth -framework CoreFoundation
-
-
-hidtest: $(OBJS)
- g++ -Wall -g $^ $(LIBS) -o hidtest
-
-$(COBJS): %.o: %.c
- $(CC) $(CFLAGS) $< -o $@
-
-$(CPPOBJS): %.o: %.cpp
- $(CXX) $(CFLAGS) $< -o $@
-
-clean:
- rm -f *.o hidtest $(CPPOBJS)
-
-.PHONY: clean
diff --git a/src/hidapi/ios/Makefile.am b/src/hidapi/ios/Makefile.am
deleted file mode 100644
index 1f8f2ce..0000000
--- a/src/hidapi/ios/Makefile.am
+++ /dev/null
@@ -1,9 +0,0 @@
-lib_LTLIBRARIES = libhidapi.la
-libhidapi_la_SOURCES = hid.m
-libhidapi_la_LDFLAGS = $(LTLDFLAGS)
-AM_CPPFLAGS = -I$(top_srcdir)/hidapi/
-
-hdrdir = $(includedir)/hidapi
-hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h
-
-EXTRA_DIST = Makefile-manual
diff --git a/src/hidapi/ios/hid.m b/src/hidapi/ios/hid.m
deleted file mode 100644
index 2748d8c..0000000
--- a/src/hidapi/ios/hid.m
+++ /dev/null
@@ -1,996 +0,0 @@
-/*
- Simple DirectMedia Layer
- Copyright (C) 2021 Valve Corporation
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-*/
-#include "SDL_internal.h"
-
-#ifndef SDL_HIDAPI_DISABLED
-
-
-#define hid_init PLATFORM_hid_init
-#define hid_exit PLATFORM_hid_exit
-#define hid_enumerate PLATFORM_hid_enumerate
-#define hid_free_enumeration PLATFORM_hid_free_enumeration
-#define hid_open PLATFORM_hid_open
-#define hid_open_path PLATFORM_hid_open_path
-#define hid_write PLATFORM_hid_write
-#define hid_read_timeout PLATFORM_hid_read_timeout
-#define hid_read PLATFORM_hid_read
-#define hid_set_nonblocking PLATFORM_hid_set_nonblocking
-#define hid_send_feature_report PLATFORM_hid_send_feature_report
-#define hid_get_feature_report PLATFORM_hid_get_feature_report
-#define hid_close PLATFORM_hid_close
-#define hid_get_manufacturer_string PLATFORM_hid_get_manufacturer_string
-#define hid_get_product_string PLATFORM_hid_get_product_string
-#define hid_get_serial_number_string PLATFORM_hid_get_serial_number_string
-#define hid_get_indexed_string PLATFORM_hid_get_indexed_string
-#define hid_error PLATFORM_hid_error
-
-#include <CoreBluetooth/CoreBluetooth.h>
-#include <QuartzCore/QuartzCore.h>
-#import <UIKit/UIKit.h>
-#import <mach/mach_time.h>
-#include <pthread.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include "../hidapi/hidapi.h"
-
-#define VALVE_USB_VID 0x28DE
-#define D0G_BLE2_PID 0x1106
-
-typedef uint32_t uint32;
-typedef uint64_t uint64;
-
-// enables detailed NSLog logging of feature reports
-#define FEATURE_REPORT_LOGGING 0
-
-#define REPORT_SEGMENT_DATA_FLAG 0x80
-#define REPORT_SEGMENT_LAST_FLAG 0x40
-
-#define VALVE_SERVICE @"100F6C32-1735-4313-B402-38567131E5F3"
-
-// (READ/NOTIFICATIONS)
-#define VALVE_INPUT_CHAR @"100F6C33-1735-4313-B402-38567131E5F3"
-
-// (READ/WRITE)
-#define VALVE_REPORT_CHAR @"100F6C34-1735-4313-B402-38567131E5F3"
-
-// TODO: create CBUUID's in __attribute__((constructor)) rather than doing [CBUUID UUIDWithString:...] everywhere
-
-#pragma pack(push,1)
-
-typedef struct
-{
- uint8_t segmentHeader;
- uint8_t featureReportMessageID;
- uint8_t length;
- uint8_t settingIdentifier;
- union {
- uint16_t usPayload;
- uint32_t uPayload;
- uint64_t ulPayload;
- uint8_t ucPayload[15];
- };
-} bluetoothSegment;
-
-typedef struct {
- uint8_t id;
- union {
- bluetoothSegment segment;
- struct {
- uint8_t segmentHeader;
- uint8_t featureReportMessageID;
- uint8_t length;
- uint8_t settingIdentifier;
- union {
- uint16_t usPayload;
- uint32_t uPayload;
- uint64_t ulPayload;
- uint8_t ucPayload[15];
- };
- };
- };
-} hidFeatureReport;
-
-#pragma pack(pop)
-
-size_t GetBluetoothSegmentSize(bluetoothSegment *segment)
-{
- return segment->length + 3;
-}
-
-#define RingBuffer_cbElem 19
-#define RingBuffer_nElem 4096
-
-typedef struct {
- int _first, _last;
- uint8_t _data[ ( RingBuffer_nElem * RingBuffer_cbElem ) ];
- pthread_mutex_t accessLock;
-} RingBuffer;
-
-static void RingBuffer_init( RingBuffer *this )
-{
- this->_first = -1;
- this->_last = 0;
- pthread_mutex_init( &this->accessLock, 0 );
-}
-
-static bool RingBuffer_write( RingBuffer *this, const uint8_t *src )
-{
- pthread_mutex_lock( &this->accessLock );
- memcpy( &this->_data[ this->_last ], src, RingBuffer_cbElem );
- if ( this->_first == -1 )
- {
- this->_first = this->_last;
- }
- this->_last = ( this->_last + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem);
- if ( this->_last == this->_first )
- {
- this->_first = ( this->_first + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem);
- pthread_mutex_unlock( &this->accessLock );
- return false;
- }
- pthread_mutex_unlock( &this->accessLock );
- return true;
-}
-
-static bool RingBuffer_read( RingBuffer *this, uint8_t *dst )
-{
- pthread_mutex_lock( &this->accessLock );
- if ( this->_first == -1 )
- {
- pthread_mutex_unlock( &this->accessLock );
- return false;
- }
- memcpy( dst, &this->_data[ this->_first ], RingBuffer_cbElem );
- this->_first = ( this->_first + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem);
- if ( this->_first == this->_last )
- {
- this->_first = -1;
- }
- pthread_mutex_unlock( &this->accessLock );
- return true;
-}
-
-
-#pragma mark HIDBLEDevice Definition
-
-typedef enum
-{
- BLEDeviceWaitState_None,
- BLEDeviceWaitState_Waiting,
- BLEDeviceWaitState_Complete,
- BLEDeviceWaitState_Error
-} BLEDeviceWaitState;
-
-@interface HIDBLEDevice : NSObject <CBPeripheralDelegate>
-{
- RingBuffer _inputReports;
- uint8_t _featureReport[20];
- BLEDeviceWaitState _waitStateForReadFeatureReport;
- BLEDeviceWaitState _waitStateForWriteFeatureReport;
-}
-
-@property (nonatomic, readwrite) bool connected;
-@property (nonatomic, readwrite) bool ready;
-
-@property (nonatomic, strong) CBPeripheral *bleSteamController;
-@property (nonatomic, strong) CBCharacteristic *bleCharacteristicInput;
-@property (nonatomic, strong) CBCharacteristic *bleCharacteristicReport;
-
-- (id)initWithPeripheral:(CBPeripheral *)peripheral;
-
-@end
-
-
-@interface HIDBLEManager : NSObject <CBCentralManagerDelegate>
-
-@property (nonatomic) int nPendingScans;
-@property (nonatomic) int nPendingPairs;
-@property (nonatomic, strong) CBCentralManager *centralManager;
-@property (nonatomic, strong) NSMapTable<CBPeripheral *, HIDBLEDevice *> *deviceMap;
-@property (nonatomic, retain) dispatch_queue_t bleSerialQueue;
-
-+ (instancetype)sharedInstance;
-- (void)startScan:(int)duration;
-- (void)stopScan;
-- (int)updateConnectedSteamControllers:(BOOL) bForce;
-- (void)appWillResignActiveNotification:(NSNotification *)note;
-- (void)appDidBecomeActiveNotification:(NSNotification *)note;
-
-@end
-
-
-// singleton class - access using HIDBLEManager.sharedInstance
-@implementation HIDBLEManager
-
-+ (instancetype)sharedInstance
-{
- static HIDBLEManager *sharedInstance = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- sharedInstance = [HIDBLEManager new];
- sharedInstance.nPendingScans = 0;
- sharedInstance.nPendingPairs = 0;
-
- // Bluetooth is currently only used for Steam Controllers, so check that hint
- // before initializing Bluetooth, which will prompt the user for permission.
- if ( SDL_GetHintBoolean( SDL_HINT_JOYSTICK_HIDAPI_STEAM, SDL_FALSE ) )
- {
- [[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(appWillResignActiveNotification:) name: UIApplicationWillResignActiveNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(appDidBecomeActiveNotification:) name:UIApplicationDidBecomeActiveNotification object:nil];
-
- // receive reports on a high-priority serial-queue. optionally put writes on the serial queue to avoid logical
- // race conditions talking to the controller from multiple threads, although BLE fragmentation/assembly means
- // that we can still screw this up.
- // most importantly we need to consume reports at a high priority to avoid the OS thinking we aren't really
- // listening to the BLE device, as iOS on slower devices may stop delivery of packets to the app WITHOUT ACTUALLY
- // DISCONNECTING FROM THE DEVICE if we don't react quickly enough to their delivery.
- // see also the error-handling states in the peripheral delegate to re-open the device if it gets closed
- sharedInstance.bleSerialQueue = dispatch_queue_create( "com.valvesoftware.steamcontroller.ble", DISPATCH_QUEUE_SERIAL );
- dispatch_set_target_queue( sharedInstance.bleSerialQueue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) );
-
- // creating a CBCentralManager will always trigger a future centralManagerDidUpdateState:
- // where any scanning gets started or connecting to existing peripherals happens, it's never already in a
- // powered-on state for a newly launched application.
- sharedInstance.centralManager = [[CBCentralManager alloc] initWithDelegate:sharedInstance queue:sharedInstance.bleSerialQueue];
- }
- sharedInstance.deviceMap = [[NSMapTable alloc] initWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableStrongMemory capacity:4];
- });
- return sharedInstance;
-}
-
-// called for NSNotification UIApplicationWillResignActiveNotification
-- (void)appWillResignActiveNotification:(NSNotification *)note
-{
- // we'll get resign-active notification if pairing is happening.
- if ( self.nPendingPairs > 0 )
- return;
-
- for ( CBPeripheral *peripheral in self.deviceMap )
- {
- HIDBLEDevice *steamController = [self.deviceMap objectForKey:peripheral];
- if ( steamController )
- {
- steamController.connected = NO;
- steamController.ready = NO;
- [self.centralManager cancelPeripheralConnection:peripheral];
- }
- }
- [self.deviceMap removeAllObjects];
-}
-
-// called for NSNotification UIApplicationDidBecomeActiveNotification
-// whenever the application comes back from being inactive, trigger a 20s pairing scan and reconnect
-// any devices that may have paired while we were inactive.
-- (void)appDidBecomeActiveNotification:(NSNotification *)note
-{
- [self updateConnectedSteamControllers:true];
- [self startScan:20];
-}
-
-- (int)updateConnectedSteamControllers:(BOOL) bForce
-{
- static uint64_t s_unLastUpdateTick = 0;
- static mach_timebase_info_data_t s_timebase_info;
-
- if ( self.centralManager == nil )
- {
- return 0;
- }
-
- if (s_timebase_info.denom == 0)
- {
- mach_timebase_info( &s_timebase_info );
- }
-
- uint64_t ticksNow = mach_approximate_time();
- if ( !bForce && ( ( (ticksNow - s_unLastUpdateTick) * s_timebase_info.numer ) / s_timebase_info.denom ) < (5ull * NSEC_PER_SEC) )
- return (int)self.deviceMap.count;
-
- // we can see previously connected BLE peripherals but can't connect until the CBCentralManager
- // is fully powered up - only do work when we are in that state
- if ( self.centralManager.state != CBManagerStatePoweredOn )
- return (int)self.deviceMap.count;
-
- // only update our last-check-time if we actually did work, otherwise there can be a long delay during initial power-up
- s_unLastUpdateTick = mach_approximate_time();
-
- // if a pair is in-flight, the central manager may still give it back via retrieveConnected... and
- // cause the SDL layer to attempt to initialize it while some of its endpoints haven't yet been established
- if ( self.nPendingPairs > 0 )
- return (int)self.deviceMap.count;
-
- NSArray<CBPeripheral *> *peripherals = [self.centralManager retrieveConnectedPeripheralsWithServices: @[ [CBUUID UUIDWithString:@"180A"]]];
- for ( CBPeripheral *peripheral in peripherals )
- {
- // we already know this peripheral
- if ( [self.deviceMap objectForKey: peripheral] != nil )
- continue;
-
- NSLog( @"connected peripheral: %@", peripheral );
- if ( [peripheral.name isEqualToString:@"SteamController"] )
- {
- self.nPendingPairs += 1;
- HIDBLEDevice *steamController = [[HIDBLEDevice alloc] initWithPeripheral:peripheral];
- [self.deviceMap setObject:steamController forKey:peripheral];
- [self.centralManager connectPeripheral:peripheral options:nil];
- }
- }
-
- return (int)self.deviceMap.count;
-}
-
-// manual API for folks to start & stop scanning
-- (void)startScan:(int)duration
-{
- if ( self.centralManager == nil )
- {
- return;
- }
-
- NSLog( @"BLE: requesting scan for %d seconds", duration );
- @synchronized (self)
- {
- if ( _nPendingScans++ == 0 )
- {
- [self.centralManager scanForPeripheralsWithServices:nil options:nil];
- }
- }
-
- if ( duration != 0 )
- {
- dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [self stopScan];
- });
- }
-}
-
-- (void)stopScan
-{
- if ( self.centralManager == nil )
- {
- return;
- }
-
- NSLog( @"BLE: stopping scan" );
- @synchronized (self)
- {
- if ( --_nPendingScans <= 0 )
- {
- _nPendingScans = 0;
- [self.centralManager stopScan];
- }
- }
-}
-
-
-#pragma mark CBCentralManagerDelegate Implementation
-
-// called whenever the BLE hardware state changes.
-- (void)centralManagerDidUpdateState:(CBCentralManager *)central
-{
- switch ( central.state )
- {
- case CBCentralManagerStatePoweredOn:
- {
- NSLog( @"CoreBluetooth BLE hardware is powered on and ready" );
-
- // at startup, if we have no already attached peripherals, do a 20s scan for new unpaired devices,
- // otherwise callers should occaisionally do additional scans. we don't want to continuously be
- // scanning because it drains battery, causes other nearby people to have a hard time pairing their
- // Steam Controllers, and may also trigger firmware weirdness when a device attempts to start
- // the pairing sequence multiple times concurrently
- if ( [self updateConnectedSteamControllers:false] == 0 )
- {
- // TODO: we could limit our scan to only peripherals supporting the SteamController service, but
- // that service doesn't currently fit in the base advertising packet, we'd need to put it into an
- // extended scan packet. Useful optimization downstream, but not currently necessary
- // NSArray *services = @[[CBUUID UUIDWithString:VALVE_SERVICE]];
- [self startScan:20];
- }
- break;
- }
-
- case CBCentralManagerStatePoweredOff:
- NSLog( @"CoreBluetooth BLE hardware is powered off" );
- break;
-
- case CBCentralManagerStateUnauthorized:
- NSLog( @"CoreBluetooth BLE state is unauthorized" );
- break;
-
- case CBCentralManagerStateUnknown:
- NSLog( @"CoreBluetooth BLE state is unknown" );
- break;
-
- case CBCentralManagerStateUnsupported:
- NSLog( @"CoreBluetooth BLE hardware is unsupported on this platform" );
- break;
-
- case CBCentralManagerStateResetting:
- NSLog( @"CoreBluetooth BLE manager is resetting" );
- break;
- }
-}
-
-- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
-{
- HIDBLEDevice *steamController = [_deviceMap objectForKey:peripheral];
- steamController.connected = YES;
- self.nPendingPairs -= 1;
-}
-
-- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
-{
- NSLog( @"Failed to connect: %@", error );
- [_deviceMap removeObjectForKey:peripheral];
- self.nPendingPairs -= 1;
-}
-
-- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
-{
- NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
- NSString *log = [NSString stringWithFormat:@"Found '%@'", localName];
-
- if ( [localName isEqualToString:@"SteamController"] )
- {
- NSLog( @"%@ : %@ - %@", log, peripheral, advertisementData );
- self.nPendingPairs += 1;
- HIDBLEDevice *steamController = [[HIDBLEDevice alloc] initWithPeripheral:peripheral];
- [self.deviceMap setObject:steamController forKey:peripheral];
- [self.centralManager connectPeripheral:peripheral options:nil];
- }
-}
-
-- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
-{
- HIDBLEDevice *steamController = [self.deviceMap objectForKey:peripheral];
- if ( steamController )
- {
- steamController.connected = NO;
- steamController.ready = NO;
- [self.deviceMap removeObjectForKey:peripheral];
- }
-}
-
-@end
-
-
-// Core Bluetooth devices calling back on event boundaries of their run-loops. so annoying.
-static void process_pending_events()
-{
- CFRunLoopRunResult res;
- do
- {
- res = CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0.001, FALSE );
- }
- while( res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut );
-}
-
-@implementation HIDBLEDevice
-
-- (id)init
-{
- if ( self = [super init] )
- {
- RingBuffer_init( &_inputReports );
- self.bleSteamController = nil;
- self.bleCharacteristicInput = nil;
- self.bleCharacteristicReport = nil;
- _connected = NO;
- _ready = NO;
- }
- return self;
-}
-
-- (id)initWithPeripheral:(CBPeripheral *)peripheral
-{
- if ( self = [super init] )
- {
- RingBuffer_init( &_inputReports );
- _connected = NO;
- _ready = NO;
- self.bleSteamController = peripheral;
- if ( peripheral )
- {
- peripheral.delegate = self;
- }
- self.bleCharacteristicInput = nil;
- self.bleCharacteristicReport = nil;
- }
- return self;
-}
-
-- (void)setConnected:(bool)connected
-{
- _connected = connected;
- if ( _connected )
- {
- [_bleSteamController discoverServices:nil];
- }
- else
- {
- NSLog( @"Disconnected" );
- }
-}
-
-- (size_t)read_input_report:(uint8_t *)dst
-{
- if ( RingBuffer_read( &_inputReports, dst+1 ) )
- {
- *dst = 0x03;
- return 20;
- }
- return 0;
-}
-
-- (int)send_report:(const uint8_t *)data length:(size_t)length
-{
- [_bleSteamController writeValue:[NSData dataWithBytes:data length:length] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
- return (int)length;
-}
-
-- (int)send_feature_report:(hidFeatureReport *)report
-{
-#if FEATURE_REPORT_LOGGING
- uint8_t *reportBytes = (uint8_t *)report;
-
- NSLog( @"HIDBLE:send_feature_report (%02zu/19) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", GetBluetoothSegmentSize( report->segment ),
- reportBytes[1], reportBytes[2], reportBytes[3], reportBytes[4], reportBytes[5], reportBytes[6],
- reportBytes[7], reportBytes[8], reportBytes[9], reportBytes[10], reportBytes[11], reportBytes[12],
- reportBytes[13], reportBytes[14], reportBytes[15], reportBytes[16], reportBytes[17], reportBytes[18],
- reportBytes[19] );
-#endif
-
- int sendSize = (int)GetBluetoothSegmentSize( &report->segment );
- if ( sendSize > 20 )
- sendSize = 20;
-
-#if 1
- // fire-and-forget - we are going to not wait for the response here because all Steam Controller BLE send_feature_report's are ignored,
- // except errors.
- [_bleSteamController writeValue:[NSData dataWithBytes:&report->segment length:sendSize] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
-
- // pretend we received a result anybody cares about
- return 19;
-
-#else
- // this is technically the correct send_feature_report logic if you want to make sure it gets through and is
- // acknowledged or errors out
- _waitStateForWriteFeatureReport = BLEDeviceWaitState_Waiting;
- [_bleSteamController writeValue:[NSData dataWithBytes:&report->segment length:sendSize
- ] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
-
- while ( _waitStateForWriteFeatureReport == BLEDeviceWaitState_Waiting )
- {
- process_pending_events();
- }
-
- if ( _waitStateForWriteFeatureReport == BLEDeviceWaitState_Error )
- {
- _waitStateForWriteFeatureReport = BLEDeviceWaitState_None;
- return -1;
- }
-
- _waitStateForWriteFeatureReport = BLEDeviceWaitState_None;
- return 19;
-#endif
-}
-
-- (int)get_feature_report:(uint8_t)feature into:(uint8_t *)buffer
-{
- _waitStateForReadFeatureReport = BLEDeviceWaitState_Waiting;
- [_bleSteamController readValueForCharacteristic:_bleCharacteristicReport];
-
- while ( _waitStateForReadFeatureReport == BLEDeviceWaitState_Waiting )
- process_pending_events();
-
- if ( _waitStateForReadFeatureReport == BLEDeviceWaitState_Error )
- {
- _waitStateForReadFeatureReport = BLEDeviceWaitState_None;
- return -1;
- }
-
- memcpy( buffer, _featureReport, sizeof(_featureReport) );
-
- _waitStateForReadFeatureReport = BLEDeviceWaitState_None;
-
-#if FEATURE_REPORT_LOGGING
- NSLog( @"HIDBLE:get_feature_report (19) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]",
- buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6],
- buffer[7], buffer[8], buffer[9], buffer[10], buffer[11], buffer[12],
- buffer[13], buffer[14], buffer[15], buffer[16], buffer[17], buffer[18],
- buffer[19] );
-#endif
-
- return 19;
-}
-
-#pragma mark CBPeripheralDelegate Implementation
-
-- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
-{
- for (CBService *service in peripheral.services)
- {
- NSLog( @"Found Service: %@", service );
- if ( [service.UUID isEqual:[CBUUID UUIDWithString:VALVE_SERVICE]] )
- {
- [peripheral discoverCharacteristics:nil forService:service];
- }
- }
-}
-
-- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
-{
- // nothing yet needed here, enable for logging
- if ( /* DISABLES CODE */ (0) )
- {
- for ( CBDescriptor *descriptor in characteristic.descriptors )
- {
- NSLog( @" - Descriptor '%@'", descriptor );
- }
- }
-}
-
-- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
-{
- if ([service.UUID isEqual:[CBUUID UUIDWithString:VALVE_SERVICE]])
- {
- for (CBCharacteristic *aChar in service.characteristics)
- {
- NSLog( @"Found Characteristic %@", aChar );
-
- if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_INPUT_CHAR]] )
- {
- self.bleCharacteristicInput = aChar;
- }
- else if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_REPORT_CHAR]] )
- {
- self.bleCharacteristicReport = aChar;
- [self.bleSteamController discoverDescriptorsForCharacteristic: aChar];
- }
- }
- }
-}
-
-- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
-{
- static uint64_t s_ticksLastOverflowReport = 0;
-
- // receiving an input report is the final indicator that the user accepted a pairing
- // request and that we successfully established notification. CoreBluetooth has no
- // notification of the pairing acknowledgement, which is a bad oversight.
- if ( self.ready == NO )
- {
- self.ready = YES;
- HIDBLEManager.sharedInstance.nPendingPairs -= 1;
- }
-
- if ( [characteristic.UUID isEqual:_bleCharacteristicInput.UUID] )
- {
- NSData *data = [characteristic value];
- if ( data.length != 19 )
- {
- NSLog( @"HIDBLE: incoming data is %lu bytes should be exactly 19", (unsigned long)data.length );
- }
- if ( !RingBuffer_write( &_inputReports, (const uint8_t *)data.bytes ) )
- {
- uint64_t ticksNow = mach_approximate_time();
- if ( ticksNow - s_ticksLastOverflowReport > (5ull * NSEC_PER_SEC / 10) )
- {
- NSLog( @"HIDBLE: input report buffer overflow" );
- s_ticksLastOverflowReport = ticksNow;
- }
- }
- }
- else if ( [characteristic.UUID isEqual:_bleCharacteristicReport.UUID] )
- {
- memset( _featureReport, 0, sizeof(_featureReport) );
-
- if ( error != nil )
- {
- NSLog( @"HIDBLE: get_feature_report error: %@", error );
- _waitStateForReadFeatureReport = BLEDeviceWaitState_Error;
- }
- else
- {
- NSData *data = [characteristic value];
- if ( data.length != 20 )
- {
- NSLog( @"HIDBLE: incoming data is %lu bytes should be exactly 20", (unsigned long)data.length );
- }
- memcpy( _featureReport, data.bytes, MIN( data.length, sizeof(_featureReport) ) );
- _waitStateForReadFeatureReport = BLEDeviceWaitState_Complete;
- }
- }
-}
-
-- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
-{
- if ( [characteristic.UUID isEqual:[CBUUID UUIDWithString:VALVE_REPORT_CHAR]] )
- {
- if ( error != nil )
- {
- NSLog( @"HIDBLE: write_feature_report error: %@", error );
- _waitStateForWriteFeatureReport = BLEDeviceWaitState_Error;
- }
- else
- {
- _waitStateForWriteFeatureReport = BLEDeviceWaitState_Complete;
- }
- }
-}
-
-- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
-{
- NSLog( @"didUpdateNotifcationStateForCharacteristic %@ (%@)", characteristic, error );
-}
-
-@end
-
-
-#pragma mark hid_api implementation
-
-struct hid_device_ {
- void *device_handle;
- int blocking;
- hid_device *next;
-};
-
-int HID_API_EXPORT HID_API_CALL hid_init(void)
-{
- return ( HIDBLEManager.sharedInstance == nil ) ? -1 : 0;
-}
-
-int HID_API_EXPORT HID_API_CALL hid_exit(void)
-{
- return 0;
-}
-
-void HID_API_EXPORT HID_API_CALL hid_ble_scan( int bStart )
-{
- HIDBLEManager *bleManager = HIDBLEManager.sharedInstance;
- if ( bStart )
- {
- [bleManager startScan:0];
- }
- else
- {
- [bleManager stopScan];
- }
-}
-
-HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
-{
- return NULL;
-}
-
-HID_API_EXPORT hid_device * HID_API_CALL hid_open_path( const char *path, int bExclusive /* = false */ )
-{
- hid_device *result = NULL;
- NSString *nssPath = [NSString stringWithUTF8String:path];
- HIDBLEManager *bleManager = HIDBLEManager.sharedInstance;
- NSEnumerator<HIDBLEDevice *> *devices = [bleManager.deviceMap objectEnumerator];
-
- for ( HIDBLEDevice *device in devices )
- {
- // we have the device but it hasn't found its service or characteristics until it is connected
- if ( !device.ready || !device.connected || !device.bleCharacteristicInput )
- continue;
-
- if ( [device.bleSteamController.identifier.UUIDString isEqualToString:nssPath] )
- {
- result = (hid_device *)malloc( sizeof( hid_device ) );
- memset( result, 0, sizeof( hid_device ) );
- result->device_handle = (void*)CFBridgingRetain( device );
- result->blocking = NO;
- // enable reporting input events on the characteristic
- [device.bleSteamController setNotifyValue:YES forCharacteristic:device.bleCharacteristicInput];
- return result;
- }
- }
- return result;
-}
-
-void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
-{
- /* This function is identical to the Linux version. Platform independent. */
- struct hid_device_info *d = devs;
- while (d) {
- struct hid_device_info *next = d->next;
- free(d->path);
- free(d->serial_number);
- free(d->manufacturer_string);
- free(d->product_string);
- free(d);
- d = next;
- }
-}
-
-int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
-{
- /* All Nonblocking operation is handled by the library. */
- dev->blocking = !nonblock;
-
- return 0;
-}
-
-struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
-{ @autoreleasepool {
- struct hid_device_info *root = NULL;
- const char *hint = SDL_GetHint(SDL_HINT_HIDAPI_IGNORE_DEVICES);
-
- /* See if there are any devices we should skip in enumeration */
- if (hint) {
- char vendor_match[16], product_match[16];
- SDL_snprintf(vendor_match, sizeof(vendor_match), "0x%.4x/0x0000", VALVE_USB_VID);
- SDL_snprintf(product_match, sizeof(product_match), "0x%.4x/0x%.4x", VALVE_USB_VID, D0G_BLE2_PID);
- if (SDL_strcasestr(hint, vendor_match) || SDL_strcasestr(hint, product_match)) {
- return NULL;
- }
- }
-
- if ( ( vendor_id == 0 && product_id == 0 ) ||
- ( vendor_id == VALVE_USB_VID && product_id == D0G_BLE2_PID ) )
- {
- HIDBLEManager *bleManager = HIDBLEManager.sharedInstance;
- [bleManager updateConnectedSteamControllers:false];
- NSEnumerator<HIDBLEDevice *> *devices = [bleManager.deviceMap objectEnumerator];
- for ( HIDBLEDevice *device in devices )
- {
- // there are several brief windows in connecting to an already paired device and
- // one long window waiting for users to confirm pairing where we don't want
- // to consider a device ready - if we hand it back to SDL or another
- // Steam Controller consumer, their additional SC setup work will fail
- // in unusual/silent ways and we can actually corrupt the BLE stack for
- // the entire system and kill the appletv remote's Menu button (!)
- if ( device.bleSteamController.state != CBPeripheralStateConnected ||
- device.connected == NO || device.ready == NO )
- {
- if ( device.ready == NO && device.bleCharacteristicInput != nil )
- {
- // attempt to register for input reports. this call will silently fail
- // until the pairing finalizes with user acceptance. oh, apple.
- [device.bleSteamController setNotifyValue:YES forCharacteristic:device.bleCharacteristicInput];
- }
- continue;
- }
- struct hid_device_info *device_info = (struct hid_device_info *)malloc( sizeof(struct hid_device_info) );
- memset( device_info, 0, sizeof(struct hid_device_info) );
- device_info->next = root;
- root = device_info;
- device_info->path = strdup( device.bleSteamController.identifier.UUIDString.UTF8String );
- device_info->vendor_id = VALVE_USB_VID;
- device_info->product_id = D0G_BLE2_PID;
- device_info->product_string = wcsdup( L"Steam Controller" );
- device_info->manufacturer_string = wcsdup( L"Valve Corporation" );
- }
- }
- return root;
-}}
-
-int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
-{
- static wchar_t s_wszManufacturer[] = L"Valve Corporation";
- wcsncpy( string, s_wszManufacturer, sizeof(s_wszManufacturer)/sizeof(s_wszManufacturer[0]) );
- return 0;
-}
-
-int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
-{
- static wchar_t s_wszProduct[] = L"Steam Controller";
- wcsncpy( string, s_wszProduct, sizeof(s_wszProduct)/sizeof(s_wszProduct[0]) );
- return 0;
-}
-
-int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
-{
- static wchar_t s_wszSerial[] = L"12345";
- wcsncpy( string, s_wszSerial, sizeof(s_wszSerial)/sizeof(s_wszSerial[0]) );
- return 0;
-}
-
-int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
-{
- return -1;
-}
-
-int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
-{
- HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
-
- if ( !device_handle.connected )
- return -1;
-
- return [device_handle send_report:data length:length];
-}
-
-void HID_API_EXPORT hid_close(hid_device *dev)
-{
- HIDBLEDevice *device_handle = CFBridgingRelease( dev->device_handle );
-
- // disable reporting input events on the characteristic
- if ( device_handle.connected ) {
- [device_handle.bleSteamController setNotifyValue:NO forCharacteristic:device_handle.bleCharacteristicInput];
- }
-
- free( dev );
-}
-
-int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
-{
- HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
-
- if ( !device_handle.connected )
- return -1;
-
- return [device_handle send_feature_report:(hidFeatureReport *)(void *)data];
-}
-
-int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
-{
- HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
-
- if ( !device_handle.connected )
- return -1;
-
- size_t written = [device_handle get_feature_report:data[0] into:data];
-
- return written == length-1 ? (int)length : (int)written;
-}
-
-int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
-{
- HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
-
- if ( !device_handle.connected )
- return -1;
-
- return hid_read_timeout(dev, data, length, 0);
-}
-
-int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
-{
- HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
-
- if ( !device_handle.connected )
- return -1;
-
- if ( milliseconds != 0 )
- {
- NSLog( @"hid_read_timeout with non-zero wait" );
- }
- int result = (int)[device_handle read_input_report:data];
-#if FEATURE_REPORT_LOGGING
- NSLog( @"HIDBLE:hid_read_timeout (%d) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", result,
- data[1], data[2], data[3], data[4], data[5], data[6],
- data[7], data[8], data[9], data[10], data[11], data[12],
- data[13], data[14], data[15], data[16], data[17], data[18],
- data[19] );
-#endif
- return result;
-}
-
-HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev)
-{
- return NULL;
-}
-
-#endif /* !SDL_HIDAPI_DISABLED */
diff --git a/src/hidapi/libusb/.gitignore b/src/hidapi/libusb/.gitignore
new file mode 100644
index 0000000..67460db
--- /dev/null
+++ b/src/hidapi/libusb/.gitignore
@@ -0,0 +1,8 @@
+*.o
+*.so
+*.la
+*.lo
+*.a
+.libs
+.deps
+hidtest-libusb
diff --git a/src/hidapi/libusb/CMakeLists.txt b/src/hidapi/libusb/CMakeLists.txt
new file mode 100644
index 0000000..6cd48c4
--- /dev/null
+++ b/src/hidapi/libusb/CMakeLists.txt
@@ -0,0 +1,107 @@
+cmake_minimum_required(VERSION 3.6.3 FATAL_ERROR)
+
+list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_libusb.h")
+
+add_library(hidapi_libusb
+ ${HIDAPI_PUBLIC_HEADERS}
+ hid.c
+)
+target_link_libraries(hidapi_libusb PUBLIC hidapi_include)
+
+if(TARGET usb-1.0)
+ target_link_libraries(hidapi_libusb PRIVATE usb-1.0)
+else()
+ include(FindPkgConfig)
+ pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0>=1.0.9)
+ target_link_libraries(hidapi_libusb PRIVATE PkgConfig::libusb)
+endif()
+
+find_package(Threads REQUIRED)
+target_link_libraries(hidapi_libusb PRIVATE Threads::Threads)
+
+if(HIDAPI_NO_ICONV)
+ target_compile_definitions(hidapi_libusb PRIVATE NO_ICONV)
+else()
+ if(NOT ANDROID)
+ include(CheckCSourceCompiles)
+
+ if(NOT CMAKE_VERSION VERSION_LESS 3.11)
+ message(STATUS "Check for Iconv")
+ find_package(Iconv)
+ if(Iconv_FOUND)
+ if(NOT Iconv_IS_BUILT_IN)
+ target_link_libraries(hidapi_libusb PRIVATE Iconv::Iconv)
+ set(CMAKE_REQUIRED_LIBRARIES "Iconv::Iconv")
+ if(NOT BUILD_SHARED_LIBS)
+ set(HIDAPI_NEED_EXPORT_ICONV TRUE PARENT_SCOPE)
+ endif()
+ endif()
+ else()
+ message(STATUS "Iconv Explicitly check '-liconv'")
+ # Sometime the build environment is not setup
+ # in a way CMake can find Iconv on its own by default.
+ # But if we simply link against iconv (-liconv), the build may succeed
+ # due to other compiler/link flags.
+ set(CMAKE_REQUIRED_LIBRARIES "iconv")
+ check_c_source_compiles("
+ #include <stddef.h>
+ #include <iconv.h>
+ int main() {
+ char *a, *b;
+ size_t i, j;
+ iconv_t ic;
+ ic = iconv_open(\"to\", \"from\");
+ iconv(ic, &a, &i, &b, &j);
+ iconv_close(ic);
+ }
+ "
+ Iconv_EXPLICITLY_AT_ENV)
+ if(Iconv_EXPLICITLY_AT_ENV)
+ message(STATUS "Iconv Explicitly check '-liconv' - Available")
+ target_link_libraries(hidapi_libusb PRIVATE iconv)
+ else()
+ message(FATAL_ERROR "Iconv is not found, make sure to provide it in the build environment")
+ endif()
+ endif()
+ else()
+ # otherwise there is 2 options:
+ # 1) iconv is provided by Standard C library and the build will be just fine
+ # 2) The _user_ has to provide additiona compilation options for this project/target
+ endif()
+
+ # check for error: "conflicting types for 'iconv'"
+ check_c_source_compiles("#include<iconv.h>
+ extern size_t iconv (iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
+ int main() {}"
+ HIDAPI_ICONV_CONST)
+ if(HIDAPI_ICONV_CONST)
+ target_compile_definitions(hidapi_libusb PRIVATE "ICONV_CONST=const")
+ endif()
+ else()
+ # On Android Iconv is disabled on the code level anyway, so no issue;
+ endif()
+endif()
+
+set_target_properties(hidapi_libusb
+ PROPERTIES
+ EXPORT_NAME "libusb"
+ OUTPUT_NAME "hidapi-libusb"
+ VERSION ${PROJECT_VERSION}
+ SOVERSION ${PROJECT_VERSION_MAJOR}
+ PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}"
+)
+
+# compatibility with find_package()
+add_library(hidapi::libusb ALIAS hidapi_libusb)
+# compatibility with raw library link
+add_library(hidapi-libusb ALIAS hidapi_libusb)
+
+if(HIDAPI_INSTALL_TARGETS)
+ install(TARGETS hidapi_libusb EXPORT hidapi
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hidapi"
+ )
+endif()
+
+hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi-libusb.pc.in")
diff --git a/src/hidapi/libusb/Makefile-manual b/src/hidapi/libusb/Makefile-manual
index c0fe868..0acf707 100644
--- a/src/hidapi/libusb/Makefile-manual
+++ b/src/hidapi/libusb/Makefile-manual
@@ -10,6 +10,10 @@
FILE=Makefile.freebsd
endif
+ifeq ($(OS), Haiku)
+ FILE=Makefile.haiku
+endif
+
ifeq ($(FILE), )
all:
$(error Your platform ${OS} is not supported by hidapi/libusb at this time.)
diff --git a/src/hidapi/libusb/Makefile.am b/src/hidapi/libusb/Makefile.am
index 13c9d35..6964ebb 100644
--- a/src/hidapi/libusb/Makefile.am
+++ b/src/hidapi/libusb/Makefile.am
@@ -21,7 +21,14 @@
libhidapi_la_LIBADD = $(LIBS_LIBUSB)
endif
+if OS_HAIKU
+lib_LTLIBRARIES = libhidapi.la
+libhidapi_la_SOURCES = hid.c
+libhidapi_la_LDFLAGS = $(LTLDFLAGS)
+libhidapi_la_LIBADD = $(LIBS_LIBUSB)
+endif
+
hdrdir = $(includedir)/hidapi
-hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h
+hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h hidapi_libusb.h
EXTRA_DIST = Makefile-manual
diff --git a/src/hidapi/libusb/Makefile.freebsd b/src/hidapi/libusb/Makefile.freebsd
index 5e69e77..c52b355 100644
--- a/src/hidapi/libusb/Makefile.freebsd
+++ b/src/hidapi/libusb/Makefile.freebsd
@@ -13,20 +13,16 @@
CC ?= cc
CFLAGS ?= -Wall -g -fPIC
-CXX ?= c++
-CXXFLAGS ?= -Wall -g
-
-COBJS = hid.o
-CPPOBJS = ../hidtest/hidtest.o
-OBJS = $(COBJS) $(CPPOBJS)
-INCLUDES = -I../hidapi -I/usr/local/include
+COBJS = hid.o ../hidtest/test.o
+OBJS = $(COBJS)
+INCLUDES = -I../hidapi -I. -I/usr/local/include
LDFLAGS = -L/usr/local/lib
LIBS = -lusb -liconv -pthread
# Console Test Program
hidtest: $(OBJS)
- $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS)
+ $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS)
# Shared Libs
libhidapi.so: $(COBJS)
@@ -36,9 +32,6 @@
$(COBJS): %.o: %.c
$(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@
-$(CPPOBJS): %.o: %.cpp
- $(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@
-
clean:
rm -f $(OBJS) hidtest libhidapi.so ../hidtest/hidtest.o
diff --git a/src/hidapi/libusb/Makefile.haiku b/src/hidapi/libusb/Makefile.haiku
new file mode 100644
index 0000000..b3622ad
--- /dev/null
+++ b/src/hidapi/libusb/Makefile.haiku
@@ -0,0 +1,39 @@
+###########################################
+# Simple Makefile for HIDAPI test program
+#
+# Alan Ott
+# Signal 11 Software
+# 2010-06-01
+###########################################
+
+all: hidtest libs
+
+libs: libhidapi.so
+
+CC ?= cc
+CFLAGS ?= -Wall -g -fPIC
+
+COBJS = hid.o ../hidtest/test.o
+OBJS = $(COBJS)
+INCLUDES = -I../hidapi -I. -I/usr/local/include
+LDFLAGS = -L/usr/local/lib
+LIBS = -lusb -liconv -pthread
+
+
+# Console Test Program
+hidtest: $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS)
+
+# Shared Libs
+libhidapi.so: $(COBJS)
+ $(CC) $(LDFLAGS) -shared -Wl,-soname,$@.0 $^ -o $@ $(LIBS)
+
+# Objects
+$(COBJS): %.o: %.c
+ $(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@
+
+
+clean:
+ rm -f $(OBJS) hidtest libhidapi.so ../hidtest/hidtest.o
+
+.PHONY: clean libs
diff --git a/src/hidapi/libusb/Makefile.linux b/src/hidapi/libusb/Makefile.linux
index 337b556..527f6b3 100644
--- a/src/hidapi/libusb/Makefile.linux
+++ b/src/hidapi/libusb/Makefile.linux
@@ -13,23 +13,19 @@
CC ?= gcc
CFLAGS ?= -Wall -g -fpic
-CXX ?= g++
-CXXFLAGS ?= -Wall -g -fpic
-
LDFLAGS ?= -Wall -g
COBJS_LIBUSB = hid.o
-COBJS = $(COBJS_LIBUSB)
-CPPOBJS = ../hidtest/hidtest.o
-OBJS = $(COBJS) $(CPPOBJS)
+COBJS = $(COBJS_LIBUSB) ../hidtest/test.o
+OBJS = $(COBJS)
LIBS_USB = `pkg-config libusb-1.0 --libs` -lrt -lpthread
LIBS = $(LIBS_USB)
-INCLUDES ?= -I../hidapi `pkg-config libusb-1.0 --cflags`
+INCLUDES ?= -I../hidapi -I. `pkg-config libusb-1.0 --cflags`
# Console Test Program
-hidtest-libusb: $(COBJS_LIBUSB) $(CPPOBJS)
- $(CXX) $(LDFLAGS) $^ $(LIBS_USB) -o $@
+hidtest-libusb: $(COBJS)
+ $(CC) $(LDFLAGS) $^ $(LIBS_USB) -o $@
# Shared Libs
libhidapi-libusb.so: $(COBJS_LIBUSB)
@@ -39,9 +35,6 @@
$(COBJS): %.o: %.c
$(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@
-$(CPPOBJS): %.o: %.cpp
- $(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@
-
clean:
rm -f $(OBJS) hidtest-libusb libhidapi-libusb.so ../hidtest/hidtest.o
diff --git a/src/hidapi/libusb/hid.c b/src/hidapi/libusb/hid.c
index f9a8b33..188e536 100644
--- a/src/hidapi/libusb/hid.c
+++ b/src/hidapi/libusb/hid.c
@@ -5,12 +5,9 @@
Alan Ott
Signal 11 Software
- 8/22/2009
- Linux Version - 6/2/2010
- Libusb Version - 8/13/2010
- FreeBSD Version - 11/1/2011
+ libusb/hidapi Team
- Copyright 2009, All Rights Reserved.
+ Copyright 2022, All Rights Reserved.
At the discretion of the user of this library,
this software may be licensed under the terms of the
@@ -23,124 +20,99 @@
https://github.com/libusb/hidapi .
********************************************************/
-/* This file is heavily modified from the original libusb.c, for portability.
- * Last upstream update was from July 25, 2019, Git commit 93dca807.
- */
+#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */
-#include "SDL_internal.h"
-#include "../../thread/SDL_systhread.h"
+/* C */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <locale.h>
+#include <errno.h>
-#ifdef realloc
-#undef realloc
-#endif
-#define realloc SDL_realloc
-#ifdef snprintf
-#undef snprintf
-#endif
-#define snprintf SDL_snprintf
-#ifdef strdup
-#undef strdup
-#endif
-#define strdup SDL_strdup
-#ifdef strncpy
-#undef strncpy
-#endif
-#define strncpy SDL_strlcpy
-#ifdef tolower
-#undef tolower
-#endif
-#define tolower SDL_tolower
-#ifdef wcsncpy
-#undef wcsncpy
-#endif
-#define wcsncpy SDL_wcslcpy
+/* Unix */
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <wchar.h>
-#ifndef HAVE_WCSDUP
-#ifdef HAVE__WCSDUP
-#define wcsdup _wcsdup
-#else
-#define wcsdup _dupwcs
-static wchar_t *_dupwcs(const wchar_t *src)
-{
- wchar_t *dst = NULL;
- if (src) {
- size_t len = SDL_wcslen(src) + 1;
- len *= sizeof(wchar_t);
- dst = (wchar_t *) malloc(len);
- if (dst) memcpy(dst, src, len);
- }
- return dst;
-}
-#endif
-#endif /* HAVE_WCSDUP */
-
+/* GNU / LibUSB */
#include <libusb.h>
-#ifndef _WIN32
-#define HAVE_SETLOCALE
-#include <locale.h> /* setlocale */
+#if !defined(__ANDROID__) && !defined(NO_ICONV)
+#include <iconv.h>
+#ifndef ICONV_CONST
+#define ICONV_CONST
+#endif
#endif
-#include "../hidapi/hidapi.h"
+#include "hidapi_libusb.h"
-#ifdef NAMESPACE
-namespace NAMESPACE
-{
-#endif
+#if defined(__ANDROID__) && __ANDROID_API__ < __ANDROID_API_N__
/* Barrier implementation because Android/Bionic don't have pthread_barrier.
This implementation came from Brent Priddy and was posted on
StackOverflow. It is used with his permission. */
+typedef int pthread_barrierattr_t;
+typedef struct pthread_barrier {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ int count;
+ int trip_count;
+} pthread_barrier_t;
-typedef struct _SDL_ThreadBarrier
+static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
{
- SDL_Mutex *mutex;
- SDL_Condition *cond;
- Uint32 count;
- Uint32 trip_count;
-} SDL_ThreadBarrier;
-
-static int SDL_CreateThreadBarrier(SDL_ThreadBarrier *barrier, Uint32 count)
-{
- SDL_assert(barrier != NULL);
- SDL_assert(count != 0);
-
- barrier->mutex = SDL_CreateMutex();
- if (barrier->mutex == NULL) {
- return -1; /* Error set by CreateMutex */
- }
- barrier->cond = SDL_CreateCondition();
- if (barrier->cond == NULL) {
- return -1; /* Error set by CreateCond */
+ if(count == 0) {
+ errno = EINVAL;
+ return -1;
}
+ if(pthread_mutex_init(&barrier->mutex, 0) < 0) {
+ return -1;
+ }
+ if(pthread_cond_init(&barrier->cond, 0) < 0) {
+ pthread_mutex_destroy(&barrier->mutex);
+ return -1;
+ }
barrier->trip_count = count;
barrier->count = 0;
return 0;
}
-static void SDL_DestroyThreadBarrier(SDL_ThreadBarrier *barrier)
+static int pthread_barrier_destroy(pthread_barrier_t *barrier)
{
- SDL_DestroyCondition(barrier->cond);
- SDL_DestroyMutex(barrier->mutex);
-}
-
-static int SDL_WaitThreadBarrier(SDL_ThreadBarrier *barrier)
-{
- SDL_LockMutex(barrier->mutex);
- barrier->count += 1;
- if (barrier->count >= barrier->trip_count) {
- barrier->count = 0;
- SDL_BroadcastCondition(barrier->cond);
- SDL_UnlockMutex(barrier->mutex);
- return 1;
- }
- SDL_WaitCondition(barrier->cond, barrier->mutex);
- SDL_UnlockMutex(barrier->mutex);
+ pthread_cond_destroy(&barrier->cond);
+ pthread_mutex_destroy(&barrier->mutex);
return 0;
}
-#if defined(__cplusplus) && !defined(NAMESPACE)
+static int pthread_barrier_wait(pthread_barrier_t *barrier)
+{
+ pthread_mutex_lock(&barrier->mutex);
+ ++(barrier->count);
+ if(barrier->count >= barrier->trip_count)
+ {
+ barrier->count = 0;
+ pthread_cond_broadcast(&barrier->cond);
+ pthread_mutex_unlock(&barrier->mutex);
+ return 1;
+ }
+ else
+ {
+ pthread_cond_wait(&barrier->cond, &(barrier->mutex));
+ pthread_mutex_unlock(&barrier->mutex);
+ return 0;
+ }
+}
+
+#endif
+
+#ifdef __cplusplus
extern "C" {
#endif
@@ -174,38 +146,49 @@
/* Handle to the actual device. */
libusb_device_handle *device_handle;
+ /* USB Configuration Number of the device */
+ int config_number;
+ /* The interface number of the HID */
+ int interface;
+
+ uint16_t report_descriptor_size;
+
/* Endpoint information */
int input_endpoint;
int output_endpoint;
int input_ep_max_packet_size;
- /* The interface number of the HID */
- int interface;
- int detached_driver;
-
/* Indexes of Strings */
int manufacturer_index;
int product_index;
int serial_index;
+ struct hid_device_info* device_info;
/* Whether blocking reads are used */
int blocking; /* boolean */
/* Read thread objects */
- SDL_Thread *thread;
- SDL_Mutex *mutex; /* Protects input_reports */
- SDL_Condition *condition;
- SDL_ThreadBarrier barrier; /* Ensures correct startup sequence */
+ pthread_t thread;
+ pthread_mutex_t mutex; /* Protects input_reports */
+ pthread_cond_t condition;
+ pthread_barrier_t barrier; /* Ensures correct startup sequence */
int shutdown_thread;
int transfer_loop_finished;
struct libusb_transfer *transfer;
- /* Quirks */
- int skip_output_report_id;
- int no_output_reports_on_intr_ep;
-
/* List of received input reports. */
struct input_report *input_reports;
+
+ /* Was kernel driver detached by libusb */
+#ifdef DETACH_KERNEL_DRIVER
+ int is_driver_detached;
+#endif
+};
+
+static struct hid_api_version api_version = {
+ .major = HID_API_VERSION_MAJOR,
+ .minor = HID_API_VERSION_MINOR,
+ .patch = HID_API_VERSION_PATCH
};
static libusb_context *usb_context = NULL;
@@ -218,9 +201,9 @@
hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
dev->blocking = 1;
- dev->mutex = SDL_CreateMutex();
- dev->condition = SDL_CreateCondition();
- SDL_CreateThreadBarrier(&dev->barrier, 2);
+ pthread_mutex_init(&dev->mutex, NULL);
+ pthread_cond_init(&dev->condition, NULL);
+ pthread_barrier_init(&dev->barrier, NULL, 2);
return dev;
}
@@ -228,9 +211,11 @@
static void free_hid_device(hid_device *dev)
{
/* Clean up the thread objects */
- SDL_DestroyThreadBarrier(&dev->barrier);
- SDL_DestroyCondition(dev->condition);
- SDL_DestroyMutex(dev->mutex);
+ pthread_barrier_destroy(&dev->barrier);
+ pthread_cond_destroy(&dev->condition);
+ pthread_mutex_destroy(&dev->mutex);
+
+ hid_free_enumeration(dev->device_info);
/* Free the device itself */
free(dev);
@@ -240,10 +225,10 @@
/*TODO: Implement this function on hidapi/libusb.. */
static void register_error(hid_device *dev, const char *op)
{
+
}
#endif
-#ifdef INVASIVE_GET_USAGE
/* Get bytes from a HID Report Descriptor.
Only call with a num_bytes of 0, 1, 2, or 4. */
static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur)
@@ -319,7 +304,7 @@
/* Can't ever happen since size_code is & 0x3 */
data_len = 0;
break;
- }
+ };
key_size = 1;
}
@@ -343,7 +328,6 @@
return -1; /* failure */
}
-#endif /* INVASIVE_GET_USAGE */
#if defined(__FreeBSD__) && __FreeBSD__ < 10
/* The libusb version included in FreeBSD < 10 doesn't have this function. In
@@ -363,6 +347,7 @@
(LIBUSB_DT_STRING << 8) | descriptor_index,
lang_id, data, (uint16_t) length, 1000);
}
+
#endif
@@ -421,16 +406,15 @@
int len;
wchar_t *str = NULL;
-#ifndef NO_ICONV
+#if !defined(__ANDROID__) && !defined(NO_ICONV) /* we don't use iconv on Android, or when it is explicitly disabled */
wchar_t wbuf[256];
- SDL_iconv_t ic;
+ /* iconv variables */
+ iconv_t ic;
size_t inbytes;
size_t outbytes;
size_t res;
- const char *inptr;
+ ICONV_CONST char *inptr;
char *outptr;
-#else
- int i;
#endif
/* Determine which language to use. */
@@ -445,12 +429,12 @@
lang,
(unsigned char*)buf,
sizeof(buf));
- if (len < 0)
+ if (len < 2) /* we always skip first 2 bytes */
return NULL;
-#ifdef NO_ICONV /* original hidapi code for NO_ICONV : */
+#if defined(__ANDROID__) || defined(NO_ICONV)
- /* Bionic does not have wchar_t iconv support, so it
+ /* Bionic does not have iconv support nor wcsdup() function, so it
has to be done manually. The following code will only work for
code points that can be represented as a single UTF-16 character,
and will incorrectly convert any code points which require more
@@ -459,19 +443,21 @@
Skip over the first character (2-bytes). */
len -= 2;
str = (wchar_t*) malloc((len / 2 + 1) * sizeof(wchar_t));
+ int i;
for (i = 0; i < len / 2; i++) {
str[i] = buf[i * 2 + 2] | (buf[i * 2 + 3] << 8);
}
str[len / 2] = 0x00000000;
#else
+
/* buf does not need to be explicitly NULL-terminated because
it is only passed into iconv() which does not need it. */
/* Initialize iconv. */
- ic = SDL_iconv_open("WCHAR_T", "UTF-16LE");
- if (ic == (SDL_iconv_t)-1) {
- LOG("SDL_iconv_open() failed\n");
+ ic = iconv_open("WCHAR_T", "UTF-16LE");
+ if (ic == (iconv_t)-1) {
+ LOG("iconv_open() failed\n");
return NULL;
}
@@ -481,9 +467,9 @@
inbytes = len-2;
outptr = (char*) wbuf;
outbytes = sizeof(wbuf);
- res = SDL_iconv(ic, &inptr, &inbytes, &outptr, &outbytes);
+ res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes);
if (res == (size_t)-1) {
- LOG("SDL_iconv() failed\n");
+ LOG("iconv() failed\n");
goto err;
}
@@ -496,145 +482,73 @@
str = wcsdup(wbuf);
err:
- SDL_iconv_close(ic);
+ iconv_close(ic);
+
#endif
return str;
}
-struct usb_string_cache_entry {
- uint16_t vid;
- uint16_t pid;
- wchar_t *vendor;
- wchar_t *product;
-};
-
-static struct usb_string_cache_entry *usb_string_cache = NULL;
-static size_t usb_string_cache_size = 0;
-static size_t usb_string_cache_insert_pos = 0;
-
-static int usb_string_cache_grow()
+/**
+ Max length of the result: "000-000.000.000.000.000.000.000:000.000" (39 chars).
+ 64 is used for simplicity/alignment.
+*/
+static void get_path(char (*result)[64], libusb_device *dev, int config_number, int interface_number)
{
- struct usb_string_cache_entry *new_cache;
- size_t allocSize;
- size_t new_cache_size;
+ char *str = *result;
- new_cache_size = usb_string_cache_size + 8;
- allocSize = sizeof(struct usb_string_cache_entry) * new_cache_size;
- new_cache = (struct usb_string_cache_entry *)realloc(usb_string_cache, allocSize);
- if (!new_cache)
- return -1;
+ /* Note that USB3 port count limit is 7; use 8 here for alignment */
+ uint8_t port_numbers[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int num_ports = libusb_get_port_numbers(dev, port_numbers, 8);
- usb_string_cache = new_cache;
- usb_string_cache_size = new_cache_size;
-
- return 0;
+ if (num_ports > 0) {
+ int n = snprintf(str, sizeof("000-000"), "%u-%u", libusb_get_bus_number(dev), port_numbers[0]);
+ for (uint8_t i = 1; i < num_ports; i++) {
+ n += snprintf(&str[n], sizeof(".000"), ".%u", port_numbers[i]);
+ }
+ n += snprintf(&str[n], sizeof(":000.000"), ":%u.%u", (uint8_t)config_number, (uint8_t)interface_number);
+ str[n] = '\0';
+ } else {
+ /* Likely impossible, but check: USB3.0 specs limit number of ports to 7 and buffer size here is 8 */
+ if (num_ports == LIBUSB_ERROR_OVERFLOW) {
+ LOG("make_path() failed. buffer overflow error\n");
+ } else {
+ LOG("make_path() failed. unknown error\n");
+ }
+ str[0] = '\0';
+ }
}
-static void usb_string_cache_destroy()
-{
- size_t i;
- for (i = 0; i < usb_string_cache_insert_pos; i++) {
- free(usb_string_cache[i].vendor);
- free(usb_string_cache[i].product);
- }
- free(usb_string_cache);
-
- usb_string_cache = NULL;
- usb_string_cache_size = 0;
- usb_string_cache_insert_pos = 0;
-}
-
-static struct usb_string_cache_entry *usb_string_cache_insert()
-{
- struct usb_string_cache_entry *new_entry = NULL;
- if (usb_string_cache_insert_pos >= usb_string_cache_size) {
- if (usb_string_cache_grow() < 0)
- return NULL;
- }
- new_entry = &usb_string_cache[usb_string_cache_insert_pos];
- usb_string_cache_insert_pos++;
- return new_entry;
-}
-
-static int usb_string_can_cache(uint16_t vid, uint16_t pid)
-{
- if (!vid || !pid) {
- /* We can't cache these, they aren't unique */
- return 0;
- }
-
- if (vid == 0x0f0d && pid == 0x00dc) {
- /* HORI reuses this VID/PID for many different products */
- return 0;
- }
-
- /* We can cache these strings */
- return 1;
-}
-
-static const struct usb_string_cache_entry *usb_string_cache_find(struct libusb_device_descriptor *desc, struct libusb_device_handle *handle)
-{
- struct usb_string_cache_entry *entry = NULL;
- size_t i;
-
- /* Search for existing string cache entry */
- for (i = 0; i < usb_string_cache_insert_pos; i++) {
- entry = &usb_string_cache[i];
- if (entry->vid != desc->idVendor)
- continue;
- if (entry->pid != desc->idProduct)
- continue;
- return entry;
- }
-
- /* Not found, create one. */
- entry = usb_string_cache_insert();
- if (!entry)
- return NULL;
-
- entry->vid = desc->idVendor;
- entry->pid = desc->idProduct;
- if (desc->iManufacturer > 0)
- entry->vendor = get_usb_string(handle, desc->iManufacturer);
- else
- entry->vendor = NULL;
- if (desc->iProduct > 0)
- entry->product = get_usb_string(handle, desc->iProduct);
- else
- entry->product = NULL;
-
- return entry;
-}
-
-static char *make_path(libusb_device *dev, int interface_number)
+static char *make_path(libusb_device *dev, int config_number, int interface_number)
{
char str[64];
- snprintf(str, sizeof(str), "%04x:%04x:%02x",
- libusb_get_bus_number(dev),
- libusb_get_device_address(dev),
- interface_number);
- str[sizeof(str)-1] = '\0';
-
+ get_path(&str, dev, config_number, interface_number);
return strdup(str);
}
+HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void)
+{
+ return &api_version;
+}
+
+HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
+{
+ return HID_API_VERSION_STR;
+}
int HID_API_EXPORT hid_init(void)
{
if (!usb_context) {
+ const char *locale;
+
/* Init Libusb */
if (libusb_init(&usb_context))
return -1;
-#ifdef HAVE_SETLOCALE
/* Set the locale if it's not set. */
- {
- const char *locale = setlocale(LC_CTYPE, NULL);
- if (!locale)
- setlocale(LC_CTYPE, "");
- }
-#endif
+ locale = setlocale(LC_CTYPE, NULL);
+ if (!locale)
+ setlocale(LC_CTYPE, "");
}
return 0;
@@ -642,8 +556,6 @@
int HID_API_EXPORT hid_exit(void)
{
- usb_string_cache_destroy();
-
if (usb_context) {
libusb_exit(usb_context);
usb_context = NULL;
@@ -652,116 +564,191 @@
return 0;
}
-static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc)
+static int hid_get_report_descriptor_libusb(libusb_device_handle *handle, int interface_num, uint16_t expected_report_descriptor_size, unsigned char *buf, size_t buf_size)
{
- static const int XB360_IFACE_SUBCLASS = 93;
- static const int XB360_IFACE_PROTOCOL = 1; /* Wired */
- static const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
- static const int SUPPORTED_VENDORS[] = {
- 0x0079, /* GPD Win 2 */
- 0x044f, /* Thrustmaster */
- 0x045e, /* Microsoft */
- 0x046d, /* Logitech */
- 0x056e, /* Elecom */
- 0x06a3, /* Saitek */
- 0x0738, /* Mad Catz */
- 0x07ff, /* Mad Catz */
- 0x0e6f, /* PDP */
- 0x0f0d, /* Hori */
- 0x1038, /* SteelSeries */
- 0x11c9, /* Nacon */
- 0x12ab, /* Unknown */
- 0x1430, /* RedOctane */
- 0x146b, /* BigBen */
- 0x1532, /* Razer Sabertooth */
- 0x15e4, /* Numark */
- 0x162e, /* Joytech */
- 0x1689, /* Razer Onza */
- 0x1949, /* Lab126, Inc. */
- 0x1bad, /* Harmonix */
- 0x20d6, /* PowerA */
- 0x24c6, /* PowerA */
- 0x2c22, /* Qanba */
- 0x2dc8, /* 8BitDo */
- 0x9886, /* ASTRO Gaming */
- };
+ unsigned char tmp[HID_API_MAX_REPORT_DESCRIPTOR_SIZE];
- if (intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC &&
- intf_desc->bInterfaceSubClass == XB360_IFACE_SUBCLASS &&
- (intf_desc->bInterfaceProtocol == XB360_IFACE_PROTOCOL ||
- intf_desc->bInterfaceProtocol == XB360W_IFACE_PROTOCOL)) {
- int i;
- for (i = 0; i < sizeof(SUPPORTED_VENDORS)/sizeof(SUPPORTED_VENDORS[0]); ++i) {
- if (vendor_id == SUPPORTED_VENDORS[i]) {
- return 1;
- }
- }
+ if (expected_report_descriptor_size > HID_API_MAX_REPORT_DESCRIPTOR_SIZE)
+ expected_report_descriptor_size = HID_API_MAX_REPORT_DESCRIPTOR_SIZE;
+
+ /* Get the HID Report Descriptor.
+ See USB HID Specificatin, sectin 7.1.1
+ */
+ int res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8), interface_num, tmp, expected_report_descriptor_size, 5000);
+ if (res < 0) {
+ LOG("libusb_control_transfer() for getting the HID Report descriptor failed with %d: %s\n", res, libusb_error_name(res));
+ return -1;
}
- return 0;
+
+ if (res > (int)buf_size)
+ res = (int)buf_size;
+
+ memcpy(buf, tmp, (size_t)res);
+ return res;
}
-static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc)
+/**
+ * Requires an opened device with *claimed interface*.
+ */
+static void fill_device_info_usage(struct hid_device_info *cur_dev, libusb_device_handle *handle, int interface_num, uint16_t expected_report_descriptor_size)
{
- static const int XB1_IFACE_SUBCLASS = 71;
- static const int XB1_IFACE_PROTOCOL = 208;
- static const int SUPPORTED_VENDORS[] = {
- 0x044f, /* Thrustmaster */
- 0x045e, /* Microsoft */
- 0x0738, /* Mad Catz */
- 0x0e6f, /* PDP */
- 0x0f0d, /* Hori */
- 0x10f5, /* Turtle Beach */
- 0x1532, /* Razer Wildcat */
- 0x20d6, /* PowerA */
- 0x24c6, /* PowerA */
- 0x2dc8, /* 8BitDo */
- 0x2e24, /* Hyperkin */
- 0x3537, /* GameSir */
- };
+ unsigned char hid_report_descriptor[HID_API_MAX_REPORT_DESCRIPTOR_SIZE];
+ unsigned short page = 0, usage = 0;
- if (intf_desc->bInterfaceNumber == 0 &&
- intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC &&
- intf_desc->bInterfaceSubClass == XB1_IFACE_SUBCLASS &&
- intf_desc->bInterfaceProtocol == XB1_IFACE_PROTOCOL) {
- int i;
- for (i = 0; i < sizeof(SUPPORTED_VENDORS)/sizeof(SUPPORTED_VENDORS[0]); ++i) {
- if (vendor_id == SUPPORTED_VENDORS[i]) {
- return 1;
- }
- }
+ int res = hid_get_report_descriptor_libusb(handle, interface_num, expected_report_descriptor_size, hid_report_descriptor, sizeof(hid_report_descriptor));
+ if (res >= 0) {
+ /* Parse the usage and usage page
+ out of the report descriptor. */
+ get_usage(hid_report_descriptor, res, &page, &usage);
}
- return 0;
+
+ cur_dev->usage_page = page;
+ cur_dev->usage = usage;
}
-static int should_enumerate_interface(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc)
+#ifdef INVASIVE_GET_USAGE
+static void invasive_fill_device_info_usage(struct hid_device_info *cur_dev, libusb_device_handle *handle, int interface_num, uint16_t report_descriptor_size)
{
- //printf("Checking interface 0x%x %d/%d/%d/%d\n", vendor_id, intf_desc->bInterfaceNumber, intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol);
+ int res = 0;
- if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID)
- return 1;
+#ifdef DETACH_KERNEL_DRIVER
+ int detached = 0;
+ /* Usage Page and Usage */
+ res = libusb_kernel_driver_active(handle, interface_num);
+ if (res == 1) {
+ res = libusb_detach_kernel_driver(handle, interface_num);
+ if (res < 0)
+ LOG("Couldn't detach kernel driver, even though a kernel driver was attached.\n");
+ else
+ detached = 1;
+ }
+#endif
- /* Also enumerate Xbox 360 controllers */
- if (is_xbox360(vendor_id, intf_desc))
- return 1;
+ res = libusb_claim_interface(handle, interface_num);
+ if (res >= 0) {
+ fill_device_info_usage(cur_dev, handle, interface_num, report_descriptor_size);
- /* Also enumerate Xbox One controllers */
- if (is_xboxone(vendor_id, intf_desc))
- return 1;
+ /* Release the interface */
+ res = libusb_release_interface(handle, interface_num);
+ if (res < 0)
+ LOG("Can't release the interface.\n");
+ }
+ else
+ LOG("Can't claim interface: (%d) %s\n", res, libusb_error_name(res));
- return 0;
+#ifdef DETACH_KERNEL_DRIVER
+ /* Re-attach kernel driver if necessary. */
+ if (detached) {
+ res = libusb_attach_kernel_driver(handle, interface_num);
+ if (res < 0)
+ LOG("Couldn't re-attach kernel driver.\n");
+ }
+#endif
+}
+#endif /* INVASIVE_GET_USAGE */
+
+/**
+ * Create and fill up most of hid_device_info fields.
+ * usage_page/usage is not filled up.
+ */
+static struct hid_device_info * create_device_info_for_device(libusb_device *device, libusb_device_handle *handle, struct libusb_device_descriptor *desc, int config_number, int interface_num)
+{
+ struct hid_device_info *cur_dev = calloc(1, sizeof(struct hid_device_info));
+ if (cur_dev == NULL) {
+ return NULL;
+ }
+
+ /* VID/PID */
+ cur_dev->vendor_id = desc->idVendor;
+ cur_dev->product_id = desc->idProduct;
+
+ cur_dev->release_number = desc->bcdDevice;
+
+ cur_dev->interface_number = interface_num;
+
+ cur_dev->bus_type = HID_API_BUS_USB;
+
+ cur_dev->path = make_path(device, config_number, interface_num);
+
+ if (!handle) {
+ return cur_dev;
+ }
+
+ if (desc->iSerialNumber > 0)
+ cur_dev->serial_number = get_usb_string(handle, desc->iSerialNumber);
+
+ /* Manufacturer and Product strings */
+ if (desc->iManufacturer > 0)
+ cur_dev->manufacturer_string = get_usb_string(handle, desc->iManufacturer);
+ if (desc->iProduct > 0)
+ cur_dev->product_string = get_usb_string(handle, desc->iProduct);
+
+ return cur_dev;
+}
+
+static uint16_t get_report_descriptor_size_from_interface_descriptors(const struct libusb_interface_descriptor *intf_desc)
+{
+ int i = 0;
+ int found_hid_report_descriptor = 0;
+ uint16_t result = HID_API_MAX_REPORT_DESCRIPTOR_SIZE;
+ const unsigned char *extra = intf_desc->extra;
+ int extra_length = intf_desc->extra_length;
+
+ /*
+ "extra" contains a HID descriptor
+ See section 6.2.1 of HID 1.1 specification.
+ */
+
+ while (extra_length >= 2) { /* Descriptor header: bLength/bDescriptorType */
+ if (extra[1] == LIBUSB_DT_HID) { /* bDescriptorType */
+ if (extra_length < 6) {
+ LOG("Broken HID descriptor: not enough data\n");
+ break;
+ }
+ unsigned char bNumDescriptors = extra[5];
+ if (extra_length < (6 + 3 * bNumDescriptors)) {
+ LOG("Broken HID descriptor: not enough data for Report metadata\n");
+ break;
+ }
+ for (i = 0; i < bNumDescriptors; i++) {
+ if (extra[6 + 3 * i] == LIBUSB_DT_REPORT) {
+ result = (uint16_t)extra[6 + 3 * i + 2] << 8 | extra[6 + 3 * i + 1];
+ found_hid_report_descriptor = 1;
+ break;
+ }
+ }
+
+ if (!found_hid_report_descriptor) {
+ /* We expect to find exactly 1 HID descriptor (LIBUSB_DT_HID)
+ which should contain exactly one HID Report Descriptor metadata (LIBUSB_DT_REPORT). */
+ LOG("Broken HID descriptor: missing Report descriptor\n");
+ }
+ break;
+ }
+
+ if (extra[0] == 0) { /* bLength */
+ LOG("Broken HID Interface descriptors: zero-sized descriptor\n");
+ break;
+ }
+
+ /* Iterate over to the next Descriptor */
+ extra_length -= extra[0];
+ extra += extra[0];
+ }
+
+ return result;
}
struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
{
libusb_device **devs;
libusb_device *dev;
- libusb_device_handle *handle;
+ libusb_device_handle *handle = NULL;
ssize_t num_devs;
int i = 0;
struct hid_device_info *root = NULL; /* return object */
struct hid_device_info *cur_dev = NULL;
- const char *hint = SDL_GetHint(SDL_HINT_HIDAPI_IGNORE_DEVICES);
if(hid_init() < 0)
return NULL;
@@ -773,85 +760,48 @@
struct libusb_device_descriptor desc;
struct libusb_config_descriptor *conf_desc = NULL;
int j, k;
- int interface_num = 0;
int res = libusb_get_device_descriptor(dev, &desc);
unsigned short dev_vid = desc.idVendor;
unsigned short dev_pid = desc.idProduct;
- /* See if there are any devices we should skip in enumeration */
- if (hint) {
- char vendor_match[16], product_match[16];
- SDL_snprintf(vendor_match, sizeof(vendor_match), "0x%.4x/0x0000", dev_vid);
- SDL_snprintf(product_match, sizeof(product_match), "0x%.4x/0x%.4x", dev_vid, dev_pid);
- if (SDL_strcasestr(hint, vendor_match) || SDL_strcasestr(hint, product_match)) {
- continue;
- }
+ if ((vendor_id != 0x0 && vendor_id != dev_vid) ||
+ (product_id != 0x0 && product_id != dev_pid)) {
+ continue;
}
res = libusb_get_active_config_descriptor(dev, &conf_desc);
if (res < 0)
libusb_get_config_descriptor(dev, 0, &conf_desc);
if (conf_desc) {
-
for (j = 0; j < conf_desc->bNumInterfaces; j++) {
const struct libusb_interface *intf = &conf_desc->interface[j];
for (k = 0; k < intf->num_altsetting; k++) {
const struct libusb_interface_descriptor *intf_desc;
intf_desc = &intf->altsetting[k];
- if (should_enumerate_interface(dev_vid, intf_desc)) {
- interface_num = intf_desc->bInterfaceNumber;
+ if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) {
+ struct hid_device_info *tmp;
- /* Check the VID/PID against the arguments */
- if ((vendor_id == 0x0 || vendor_id == dev_vid) &&
- (product_id == 0x0 || product_id == dev_pid)) {
- res = libusb_open(dev, &handle);
+ res = libusb_open(dev, &handle);
- if (res >= 0) {
- struct hid_device_info *tmp;
- const struct usb_string_cache_entry *string_cache;
+#ifdef __ANDROID__
+ if (handle) {
+ /* There is (a potential) libusb Android backend, in which
+ device descriptor is not accurate up until the device is opened.
+ https://github.com/libusb/libusb/pull/874#discussion_r632801373
+ A workaround is to re-read the descriptor again.
+ Even if it is not going to be accepted into libusb master,
+ having it here won't do any harm, since reading the device descriptor
+ is as cheap as copy 18 bytes of data. */
+ libusb_get_device_descriptor(dev, &desc);
+ }
+#endif
- /* VID/PID match. Create the record. */
- tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
- if (cur_dev) {
- cur_dev->next = tmp;
- }
- else {
- root = tmp;
- }
- cur_dev = tmp;
-
- /* Fill out the record */
- cur_dev->next = NULL;
- cur_dev->path = make_path(dev, interface_num);
-
- /* Serial Number */
- if (desc.iSerialNumber > 0)
- cur_dev->serial_number =
- get_usb_string(handle, desc.iSerialNumber);
-
- /* Manufacturer and Product strings */
- if (usb_string_can_cache(dev_vid, dev_pid)) {
- string_cache = usb_string_cache_find(&desc, handle);
- if (string_cache) {
- if (string_cache->vendor) {
- cur_dev->manufacturer_string = wcsdup(string_cache->vendor);
- }
- if (string_cache->product) {
- cur_dev->product_string = wcsdup(string_cache->product);
- }
- }
- } else {
- if (desc.iManufacturer > 0)
- cur_dev->manufacturer_string =
- get_usb_string(handle, desc.iManufacturer);
- if (desc.iProduct > 0)
- cur_dev->product_string =
- get_usb_string(handle, desc.iProduct);
- }
-
+ tmp = create_device_info_for_device(dev, handle, &desc, conf_desc->bConfigurationValue, intf_desc->bInterfaceNumber);
+ if (tmp) {
#ifdef INVASIVE_GET_USAGE
-{
+ /* TODO: have a runtime check for this section. */
+
/*
This section is removed because it is too
invasive on the system. Getting a Usage Page
@@ -864,72 +814,28 @@
app has them claimed) and the re-attachment of
the driver will sometimes change /dev entry names.
It is for these reasons that this section is
- #if 0. For composite devices, use the interface
+ optional. For composite devices, use the interface
field in the hid_device_info struct to distinguish
between interfaces. */
- unsigned char data[256];
-#ifdef DETACH_KERNEL_DRIVER
- int detached = 0;
- /* Usage Page and Usage */
- res = libusb_kernel_driver_active(handle, interface_num);
- if (res == 1) {
- res = libusb_detach_kernel_driver(handle, interface_num);
- if (res < 0)
- LOG("Couldn't detach kernel driver, even though a kernel driver was attached.");
- else
- detached = 1;
- }
-#endif
- res = libusb_claim_interface(handle, interface_num);
- if (res >= 0) {
- /* Get the HID Report Descriptor. */
- res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000);
- if (res >= 0) {
- unsigned short page=0, usage=0;
- /* Parse the usage and usage page
- out of the report descriptor. */
- get_usage(data, res, &page, &usage);
- cur_dev->usage_page = page;
- cur_dev->usage = usage;
- }
- else
- LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res);
+ if (handle) {
+ uint16_t report_descriptor_size = get_report_descriptor_size_from_interface_descriptors(intf_desc);
- /* Release the interface */
- res = libusb_release_interface(handle, interface_num);
- if (res < 0)
- LOG("Can't release the interface.\n");
- }
- else
- LOG("Can't claim interface %d\n", res);
-#ifdef DETACH_KERNEL_DRIVER
- /* Re-attach kernel driver if necessary. */
- if (detached) {
- res = libusb_attach_kernel_driver(handle, interface_num);
- if (res < 0)
- LOG("Couldn't re-attach kernel driver.\n");
- }
-#endif
-}
+ invasive_fill_device_info_usage(tmp, handle, intf_desc->bInterfaceNumber, report_descriptor_size);
+ }
#endif /* INVASIVE_GET_USAGE */
- libusb_close(handle);
+ if (cur_dev) {
+ cur_dev->next = tmp;
+ }
+ else {
+ root = tmp;
+ }
+ cur_dev = tmp;
+ }
- /* VID/PID */
- cur_dev->vendor_id = dev_vid;
- cur_dev->product_id = dev_pid;
-
- /* Release Number */
- cur_dev->release_number = desc.bcdDevice;
-
- /* Interface Number */
- cur_dev->interface_number = interface_num;
- cur_dev->interface_class = intf_desc->bInterfaceClass;
- cur_dev->interface_subclass = intf_desc->bInterfaceSubClass;
- cur_dev->interface_protocol = intf_desc->bInterfaceProtocol;
-
- } else
- LOG("Can't open device 0x%.4x/0x%.4x during enumeration: %d\n", dev_vid, dev_pid, res);
+ if (res >= 0) {
+ libusb_close(handle);
+ handle = NULL;
}
}
} /* altsettings */
@@ -985,7 +891,7 @@
if (path_to_open) {
/* Open the device */
- handle = hid_open_path(path_to_open, 0);
+ handle = hid_open_path(path_to_open);
}
hid_free_enumeration(devs);
@@ -993,9 +899,9 @@
return handle;
}
-static void LIBUSB_CALL read_callback(struct libusb_transfer *transfer)
+static void read_callback(struct libusb_transfer *transfer)
{
- hid_device *dev = (hid_device *)transfer->user_data;
+ hid_device *dev = transfer->user_data;
int res;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
@@ -1006,13 +912,13 @@
rpt->len = transfer->actual_length;
rpt->next = NULL;
- SDL_LockMutex(dev->mutex);
+ pthread_mutex_lock(&dev->mutex);
/* Attach the new report object to the end of the list. */
if (dev->input_reports == NULL) {
/* The list is empty. Put it at the root. */
dev->input_reports = rpt;
- SDL_SignalCondition(dev->condition);
+ pthread_cond_signal(&dev->condition);
}
else {
/* Find the end of the list and attach. */
@@ -1031,7 +937,7 @@
return_data(dev, NULL, 0);
}
}
- SDL_UnlockMutex(dev->mutex);
+ pthread_mutex_unlock(&dev->mutex);
}
else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
dev->shutdown_thread = 1;
@@ -1054,17 +960,17 @@
/* Re-submit the transfer object. */
res = libusb_submit_transfer(transfer);
if (res != 0) {
- LOG("Unable to submit URB. libusb error code: %d\n", res);
+ LOG("Unable to submit URB: (%d) %s\n", res, libusb_error_name(res));
dev->shutdown_thread = 1;
dev->transfer_loop_finished = 1;
}
}
-static int SDLCALL read_thread(void *param)
+static void *read_thread(void *param)
{
int res;
- hid_device *dev = (hid_device *)param;
+ hid_device *dev = param;
uint8_t *buf;
const size_t length = dev->input_ep_max_packet_size;
@@ -1090,14 +996,14 @@
}
/* Notify the main thread that the read thread is up and running. */
- SDL_WaitThreadBarrier(&dev->barrier);
+ pthread_barrier_wait(&dev->barrier);
/* Handle all the events. */
while (!dev->shutdown_thread) {
res = libusb_handle_events(usb_context);
if (res < 0) {
/* There was an error. */
- LOG("read_thread(): libusb reports error # %d\n", res);
+ LOG("read_thread(): (%d) %s\n", res, libusb_error_name(res));
/* Break out of this loop only on fatal error.*/
if (res != LIBUSB_ERROR_BUSY &&
@@ -1122,9 +1028,9 @@
make sure that a thread which is about to go to sleep waiting on
the condition actually will go to sleep before the condition is
signaled. */
- SDL_LockMutex(dev->mutex);
- SDL_BroadcastCondition(dev->condition);
- SDL_UnlockMutex(dev->mutex);
+ pthread_mutex_lock(&dev->mutex);
+ pthread_cond_broadcast(&dev->condition);
+ pthread_mutex_unlock(&dev->mutex);
/* The dev->transfer->buffer and dev->transfer objects are cleaned up
in hid_close(). They are not cleaned up here because this thread
@@ -1134,88 +1040,109 @@
since hid_close() calls libusb_cancel_transfer(), on these objects,
they can not be cleaned up here. */
- return 0;
+ return NULL;
}
-static void init_xbox360(libusb_device_handle *device_handle, unsigned short idVendor, unsigned short idProduct, struct libusb_config_descriptor *conf_desc)
+
+static int hidapi_initialize_device(hid_device *dev, int config_number, const struct libusb_interface_descriptor *intf_desc)
{
- if ((idVendor == 0x05ac && idProduct == 0x055b) /* Gamesir-G3w */ ||
- idVendor == 0x0f0d /* Hori Xbox controllers */) {
- unsigned char data[20];
+ int i =0;
+ int res = 0;
+ struct libusb_device_descriptor desc;
+ libusb_get_device_descriptor(libusb_get_device(dev->device_handle), &desc);
- /* The HORIPAD FPS for Nintendo Switch requires this to enable input reports.
- This VID/PID is also shared with other HORI controllers, but they all seem
- to be fine with this as well.
- */
- libusb_control_transfer(device_handle, 0xC1, 0x01, 0x100, 0x0, data, sizeof(data), 100);
- }
-}
-
-static void init_xboxone(libusb_device_handle *device_handle, unsigned short idVendor, unsigned short idProduct, struct libusb_config_descriptor *conf_desc)
-{
- static const int VENDOR_MICROSOFT = 0x045e;
- static const int XB1_IFACE_SUBCLASS = 71;
- static const int XB1_IFACE_PROTOCOL = 208;
- int j, k, res;
-
- for (j = 0; j < conf_desc->bNumInterfaces; j++) {
- const struct libusb_interface *intf = &conf_desc->interface[j];
- for (k = 0; k < intf->num_altsetting; k++) {
- const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k];
- if (intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC &&
- intf_desc->bInterfaceSubClass == XB1_IFACE_SUBCLASS &&
- intf_desc->bInterfaceProtocol == XB1_IFACE_PROTOCOL) {
- int bSetAlternateSetting = 0;
-
- /* Newer Microsoft Xbox One controllers have a high speed alternate setting */
- if (idVendor == VENDOR_MICROSOFT &&
- intf_desc->bInterfaceNumber == 0 && intf_desc->bAlternateSetting == 1) {
- bSetAlternateSetting = 1;
- } else if (intf_desc->bInterfaceNumber != 0 && intf_desc->bAlternateSetting == 0) {
- bSetAlternateSetting = 1;
- }
-
- if (bSetAlternateSetting) {
- res = libusb_claim_interface(device_handle, intf_desc->bInterfaceNumber);
- if (res < 0) {
- LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res);
- continue;
- }
-
- LOG("Setting alternate setting for VID/PID 0x%x/0x%x interface %d to %d\n", idVendor, idProduct, intf_desc->bInterfaceNumber, intf_desc->bAlternateSetting);
-
- res = libusb_set_interface_alt_setting(device_handle, intf_desc->bInterfaceNumber, intf_desc->bAlternateSetting);
- if (res < 0) {
- LOG("xbox init: can't set alt setting %d: %d\n", intf_desc->bInterfaceNumber, res);
- }
-
- libusb_release_interface(device_handle, intf_desc->bInterfaceNumber);
- }
- }
+#ifdef DETACH_KERNEL_DRIVER
+ /* Detach the kernel driver, but only if the
+ device is managed by the kernel */
+ dev->is_driver_detached = 0;
+ if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) {
+ res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber);
+ if (res < 0) {
+ LOG("Unable to detach Kernel Driver: (%d) %s\n", res, libusb_error_name(res));
+ return 0;
+ }
+ else {
+ dev->is_driver_detached = 1;
+ LOG("Driver successfully detached from kernel.\n");
}
}
-}
+#endif
+ res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber);
+ if (res < 0) {
+ LOG("can't claim interface %d: (%d) %s\n", intf_desc->bInterfaceNumber, res, libusb_error_name(res));
-static void calculate_device_quirks(hid_device *dev, unsigned short idVendor, unsigned short idProduct)
-{
- static const int VENDOR_SONY = 0x054c;
- static const int PRODUCT_PS3_CONTROLLER = 0x0268;
- static const int PRODUCT_NAVIGATION_CONTROLLER = 0x042f;
-
- if (idVendor == VENDOR_SONY &&
- (idProduct == PRODUCT_PS3_CONTROLLER || idProduct == PRODUCT_NAVIGATION_CONTROLLER)) {
- dev->skip_output_report_id = 1;
- dev->no_output_reports_on_intr_ep = 1;
+#ifdef DETACH_KERNEL_DRIVER
+ if (dev->is_driver_detached) {
+ res = libusb_attach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber);
+ if (res < 0)
+ LOG("Failed to reattach the driver to kernel: (%d) %s\n", res, libusb_error_name(res));
+ }
+#endif
+ return 0;
}
+
+ /* Store off the string descriptor indexes */
+ dev->manufacturer_index = desc.iManufacturer;
+ dev->product_index = desc.iProduct;
+ dev->serial_index = desc.iSerialNumber;
+
+ /* Store off the USB information */
+ dev->config_number = config_number;
+ dev->interface = intf_desc->bInterfaceNumber;
+
+ dev->report_descriptor_size = get_report_descriptor_size_from_interface_descriptors(intf_desc);
+
+ dev->input_endpoint = 0;
+ dev->input_ep_max_packet_size = 0;
+ dev->output_endpoint = 0;
+
+ /* Find the INPUT and OUTPUT endpoints. An
+ OUTPUT endpoint is not required. */
+ for (i = 0; i < intf_desc->bNumEndpoints; i++) {
+ const struct libusb_endpoint_descriptor *ep
+ = &intf_desc->endpoint[i];
+
+ /* Determine the type and direction of this
+ endpoint. */
+ int is_interrupt =
+ (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
+ == LIBUSB_TRANSFER_TYPE_INTERRUPT;
+ int is_output =
+ (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
+ == LIBUSB_ENDPOINT_OUT;
+ int is_input =
+ (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
+ == LIBUSB_ENDPOINT_IN;
+
+ /* Decide whether to use it for input or output. */
+ if (dev->input_endpoint == 0 &&
+ is_interrupt && is_input) {
+ /* Use this endpoint for INPUT */
+ dev->input_endpoint = ep->bEndpointAddress;
+ dev->input_ep_max_packet_size = ep->wMaxPacketSize;
+ }
+ if (dev->output_endpoint == 0 &&
+ is_interrupt && is_output) {
+ /* Use this endpoint for OUTPUT */
+ dev->output_endpoint = ep->bEndpointAddress;
+ }
+ }
+
+ pthread_create(&dev->thread, NULL, read_thread, dev);
+
+ /* Wait here for the read thread to be initialized. */
+ pthread_barrier_wait(&dev->barrier);
+ return 1;
}
-hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive)
+
+hid_device * HID_API_EXPORT hid_open_path(const char *path)
{
hid_device *dev = NULL;
- libusb_device **devs;
- libusb_device *usb_dev;
- int res;
+ libusb_device **devs = NULL;
+ libusb_device *usb_dev = NULL;
+ int res = 0;
int d = 0;
int good_open = 0;
@@ -1225,129 +1152,36 @@
dev = new_hid_device();
libusb_get_device_list(usb_context, &devs);
- while ((usb_dev = devs[d++]) != NULL) {
- struct libusb_device_descriptor desc;
+ while ((usb_dev = devs[d++]) != NULL && !good_open) {
struct libusb_config_descriptor *conf_desc = NULL;
- int i,j,k;
+ int j,k;
- libusb_get_device_descriptor(usb_dev, &desc);
-
- res = libusb_get_active_config_descriptor(usb_dev, &conf_desc);
- if (res < 0)
- libusb_get_config_descriptor(usb_dev, 0, &conf_desc);
- if (!conf_desc)
+ if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0)
continue;
for (j = 0; j < conf_desc->bNumInterfaces && !good_open; j++) {
const struct libusb_interface *intf = &conf_desc->interface[j];
for (k = 0; k < intf->num_altsetting && !good_open; k++) {
- const struct libusb_interface_descriptor *intf_desc;
- intf_desc = &intf->altsetting[k];
- if (should_enumerate_interface(desc.idVendor, intf_desc)) {
- char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber);
+ const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k];
+ if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) {
+ char dev_path[64];
+ get_path(&dev_path, usb_dev, conf_desc->bConfigurationValue, intf_desc->bInterfaceNumber);
if (!strcmp(dev_path, path)) {
- int detached_driver = 0;
-
/* Matched Paths. Open this device */
/* OPEN HERE */
res = libusb_open(usb_dev, &dev->device_handle);
if (res < 0) {
LOG("can't open device\n");
- free(dev_path);
break;
}
- good_open = 1;
-
-#ifdef DETACH_KERNEL_DRIVER
- /* Detach the kernel driver, but only if the
- device is managed by the kernel */
- if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) {
- res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber);
- if (res < 0) {
- libusb_close(dev->device_handle);
- LOG("Unable to detach Kernel Driver\n");
- free(dev_path);
- good_open = 0;
- break;
- }
- detached_driver = 1;
- }
-#endif
-
- res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber);
- if (res < 0) {
- LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res);
- free(dev_path);
+ good_open = hidapi_initialize_device(dev, conf_desc->bConfigurationValue, intf_desc);
+ if (!good_open)
libusb_close(dev->device_handle);
- good_open = 0;
- break;
- }
-
- /* Initialize XBox 360 controllers */
- if (is_xbox360(desc.idVendor, intf_desc)) {
- init_xbox360(dev->device_handle, desc.idVendor, desc.idProduct, conf_desc);
- }
-
- /* Initialize XBox One controllers */
- if (is_xboxone(desc.idVendor, intf_desc)) {
- init_xboxone(dev->device_handle, desc.idVendor, desc.idProduct, conf_desc);
- }
-
- /* Store off the string descriptor indexes */
- dev->manufacturer_index = desc.iManufacturer;
- dev->product_index = desc.iProduct;
- dev->serial_index = desc.iSerialNumber;
-
- /* Store off the interface number */
- dev->interface = intf_desc->bInterfaceNumber;
- dev->detached_driver = detached_driver;
-
- /* Find the INPUT and OUTPUT endpoints. An
- OUTPUT endpoint is not required. */
- for (i = 0; i < intf_desc->bNumEndpoints; i++) {
- const struct libusb_endpoint_descriptor *ep
- = &intf_desc->endpoint[i];
-
- /* Determine the type and direction of this
- endpoint. */
- int is_interrupt =
- (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
- == LIBUSB_TRANSFER_TYPE_INTERRUPT;
- int is_output =
- (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
- == LIBUSB_ENDPOINT_OUT;
- int is_input =
- (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
- == LIBUSB_ENDPOINT_IN;
-
- /* Decide whether to use it for input or output. */
- if (dev->input_endpoint == 0 &&
- is_interrupt && is_input) {
- /* Use this endpoint for INPUT */
- dev->input_endpoint = ep->bEndpointAddress;
- dev->input_ep_max_packet_size = ep->wMaxPacketSize;
- }
- if (dev->output_endpoint == 0 &&
- is_interrupt && is_output) {
- /* Use this endpoint for OUTPUT */
- dev->output_endpoint = ep->bEndpointAddress;
- }
- }
-
- calculate_device_quirks(dev, desc.idVendor, desc.idProduct);
-
- dev->thread = SDL_CreateThreadInternal(read_thread, "libusb", 0, dev);
-
- /* Wait here for the read thread to be initialized. */
- SDL_WaitThreadBarrier(&dev->barrier);
-
}
- free(dev_path);
}
}
}
libusb_free_config_descriptor(conf_desc);
-
}
libusb_free_device_list(devs, 1);
@@ -1364,20 +1198,100 @@
}
+HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys_dev, int interface_num)
+{
+/* 0x01000107 is a LIBUSB_API_VERSION for 1.0.23 - version when libusb_wrap_sys_device was introduced */
+#if (!defined(HIDAPI_TARGET_LIBUSB_API_VERSION) || HIDAPI_TARGET_LIBUSB_API_VERSION >= 0x01000107) && (LIBUSB_API_VERSION >= 0x01000107)
+ hid_device *dev = NULL;
+ struct libusb_config_descriptor *conf_desc = NULL;
+ const struct libusb_interface_descriptor *selected_intf_desc = NULL;
+ int res = 0;
+ int j = 0, k = 0;
+
+ if(hid_init() < 0)
+ return NULL;
+
+ dev = new_hid_device();
+
+ res = libusb_wrap_sys_device(usb_context, sys_dev, &dev->device_handle);
+ if (res < 0) {
+ LOG("libusb_wrap_sys_device failed: %d %s\n", res, libusb_error_name(res));
+ goto err;
+ }
+
+ res = libusb_get_active_config_descriptor(libusb_get_device(dev->device_handle), &conf_desc);
+ if (res < 0)
+ libusb_get_config_descriptor(libusb_get_device(dev->device_handle), 0, &conf_desc);
+
+ if (!conf_desc) {
+ LOG("Failed to get configuration descriptor: %d %s\n", res, libusb_error_name(res));
+ goto err;
+ }
+
+ /* find matching HID interface */
+ for (j = 0; j < conf_desc->bNumInterfaces && !selected_intf_desc; j++) {
+ const struct libusb_interface *intf = &conf_desc->interface[j];
+ for (k = 0; k < intf->num_altsetting; k++) {
+ const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k];
+ if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) {
+ if (interface_num < 0 || interface_num == intf_desc->bInterfaceNumber) {
+ selected_intf_desc = intf_desc;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!selected_intf_desc) {
+ if (interface_num < 0) {
+ LOG("Sys USB device doesn't contain a HID interface\n");
+ }
+ else {
+ LOG("Sys USB device doesn't contain a HID interface with number %d\n", interface_num);
+ }
+ goto err;
+ }
+
+ if (!hidapi_initialize_device(dev, conf_desc->bConfigurationValue, selected_intf_desc))
+ goto err;
+
+ return dev;
+
+err:
+ if (conf_desc)
+ libusb_free_config_descriptor(conf_desc);
+ if (dev->device_handle)
+ libusb_close(dev->device_handle);
+ free_hid_device(dev);
+#else
+ (void)sys_dev;
+ (void)interface_num;
+ LOG("libusb_wrap_sys_device is not available\n");
+#endif
+ return NULL;
+}
+
+
int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
{
int res;
+ int report_number;
+ int skipped_report_id = 0;
- if (dev->output_endpoint <= 0 || dev->no_output_reports_on_intr_ep) {
- int report_number = data[0];
- int skipped_report_id = 0;
+ if (!data || (length ==0)) {
+ return -1;
+ }
- if (report_number == 0x0 || dev->skip_output_report_id) {
- data++;
- length--;
- skipped_report_id = 1;
- }
+ report_number = data[0];
+ if (report_number == 0x0) {
+ data++;
+ length--;
+ skipped_report_id = 1;
+ }
+
+
+ if (dev->output_endpoint <= 0) {
/* No interrupt out endpoint. Use the Control Endpoint */
res = libusb_control_transfer(dev->device_handle,
LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT,
@@ -1407,6 +1321,9 @@
if (res < 0)
return -1;
+ if (skipped_report_id)
+ actual_length++;
+
return actual_length;
}
}
@@ -1419,7 +1336,7 @@
return buffer (data), and delete the liked list item. */
struct input_report *rpt = dev->input_reports;
size_t len = (length < rpt->len)? length: rpt->len;
- if (data && len > 0)
+ if (len > 0)
memcpy(data, rpt->data, len);
dev->input_reports = rpt->next;
free(rpt->data);
@@ -1427,13 +1344,12 @@
return len;
}
-#if 0 /* TODO: pthread_cleanup SDL? */
static void cleanup_mutex(void *param)
{
- hid_device *dev = (hid_device *)param;
- SDL_UnlockMutex(dev->mutex);
+ hid_device *dev = param;
+ pthread_mutex_unlock(&dev->mutex);
}
-#endif
+
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
{
@@ -1443,10 +1359,12 @@
LOG("transferred: %d\n", transferred);
return transferred;
#endif
- int bytes_read;
+ /* by initialising this variable right here, GCC gives a compilation warning/error: */
+ /* error: variable ‘bytes_read’ might be clobbered by ‘longjmp’ or ‘vfork’ [-Werror=clobbered] */
+ int bytes_read; /* = -1; */
- SDL_LockMutex(dev->mutex);
- /* TODO: pthread_cleanup SDL? */
+ pthread_mutex_lock(&dev->mutex);
+ pthread_cleanup_push(&cleanup_mutex, dev);
bytes_read = -1;
@@ -1467,7 +1385,7 @@
if (milliseconds == -1) {
/* Blocking */
while (!dev->input_reports && !dev->shutdown_thread) {
- SDL_WaitCondition(dev->condition, dev->mutex);
+ pthread_cond_wait(&dev->condition, &dev->mutex);
}
if (dev->input_reports) {
bytes_read = return_data(dev, data, length);
@@ -1476,9 +1394,17 @@
else if (milliseconds > 0) {
/* Non-blocking, but called with timeout. */
int res;
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += milliseconds / 1000;
+ ts.tv_nsec += (milliseconds % 1000) * 1000000;
+ if (ts.tv_nsec >= 1000000000L) {
+ ts.tv_sec++;
+ ts.tv_nsec -= 1000000000L;
+ }
while (!dev->input_reports && !dev->shutdown_thread) {
- res = SDL_WaitConditionTimeout(dev->condition, dev->mutex, milliseconds);
+ res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts);
if (res == 0) {
if (dev->input_reports) {
bytes_read = return_data(dev, data, length);
@@ -1489,7 +1415,7 @@
or the read thread was shutdown. Run the
loop again (ie: don't break). */
}
- else if (res == SDL_MUTEX_TIMEDOUT) {
+ else if (res == ETIMEDOUT) {
/* Timed out. */
bytes_read = 0;
break;
@@ -1507,8 +1433,8 @@
}
ret:
- SDL_UnlockMutex(dev->mutex);
- /* TODO: pthread_cleanup SDL? */
+ pthread_mutex_unlock(&dev->mutex);
+ pthread_cleanup_pop(0);
return bytes_read;
}
@@ -1586,11 +1512,38 @@
return res;
}
+int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
+{
+ int res = -1;
+ int skipped_report_id = 0;
+ int report_number = data[0];
+
+ if (report_number == 0x0) {
+ /* Offset the return buffer by 1, so that the report ID
+ will remain in byte 0. */
+ data++;
+ length--;
+ skipped_report_id = 1;
+ }
+ res = libusb_control_transfer(dev->device_handle,
+ LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN,
+ 0x01/*HID get_report*/,
+ (1/*HID Input*/ << 8) | report_number,
+ dev->interface,
+ (unsigned char *)data, length,
+ 1000/*timeout millis*/);
+
+ if (res < 0)
+ return -1;
+
+ if (skipped_report_id)
+ res++;
+
+ return res;
+}
void HID_API_EXPORT hid_close(hid_device *dev)
{
- int status;
-
if (!dev)
return;
@@ -1599,7 +1552,7 @@
libusb_cancel_transfer(dev->transfer);
/* Wait for read_thread() to end. */
- SDL_WaitThread(dev->thread, &status);
+ pthread_join(dev->thread, NULL);
/* Clean up the Transfer objects allocated in read_thread(). */
free(dev->transfer->buffer);
@@ -1609,12 +1562,12 @@
/* release the interface */
libusb_release_interface(dev->device_handle, dev->interface);
+ /* reattach the kernel driver if it was detached */
#ifdef DETACH_KERNEL_DRIVER
- /* Re-attach kernel driver if necessary. */
- if (dev->detached_driver) {
+ if (dev->is_driver_detached) {
int res = libusb_attach_kernel_driver(dev->device_handle, dev->interface);
if (res < 0)
- LOG("Couldn't re-attach kernel driver.\n");
+ LOG("Failed to reattach the driver to kernel.\n");
}
#endif
@@ -1622,11 +1575,11 @@
libusb_close(dev->device_handle);
/* Clear out the queue of received reports. */
- SDL_LockMutex(dev->mutex);
+ pthread_mutex_lock(&dev->mutex);
while (dev->input_reports) {
return_data(dev, NULL, 0);
}
- SDL_UnlockMutex(dev->mutex);
+ pthread_mutex_unlock(&dev->mutex);
free_hid_device(dev);
}
@@ -1647,6 +1600,23 @@
return hid_get_indexed_string(dev, dev->serial_index, string, maxlen);
}
+HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev) {
+ if (!dev->device_info) {
+ struct libusb_device_descriptor desc;
+ libusb_device *usb_device = libusb_get_device(dev->device_handle);
+ libusb_get_device_descriptor(usb_device, &desc);
+
+ dev->device_info = create_device_info_for_device(usb_device, dev->device_handle, &desc, dev->config_number, dev->interface);
+ // device error already set by create_device_info_for_device, if any
+
+ if (dev->device_info) {
+ fill_device_info_usage(dev->device_info, dev->device_handle, dev->interface, dev->report_descriptor_size);
+ }
+ }
+
+ return dev->device_info;
+}
+
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
{
wchar_t *str;
@@ -1663,9 +1633,16 @@
}
+int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
+{
+ return hid_get_report_descriptor_libusb(dev->device_handle, dev->interface, dev->report_descriptor_size, buf, buf_size);
+}
+
+
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{
- return NULL;
+ (void)dev;
+ return L"hid_error is not implemented yet";
}
@@ -1754,7 +1731,7 @@
LANG("Lithuanian", "lt", 0x0427),
LANG("F.Y.R.O. Macedonia", "mk", 0x042F),
LANG("Malay - Malaysia", "ms_my", 0x043E),
- LANG("Malay ??? Brunei", "ms_bn", 0x083E),
+ LANG("Malay – Brunei", "ms_bn", 0x083E),
LANG("Maltese", "mt", 0x043A),
LANG("Marathi", "mr", 0x044E),
LANG("Norwegian - Bokml", "no_no", 0x0414),
@@ -1805,7 +1782,7 @@
LANG("Ukrainian", "uk", 0x0422),
LANG("Urdu", "ur", 0x0420),
LANG("Uzbek - Cyrillic", "uz_uz", 0x0843),
- LANG("Uzbek ??? Latin", "uz_uz", 0x0443),
+ LANG("Uzbek – Latin", "uz_uz", 0x0443),
LANG("Vietnamese", "vi", 0x042A),
LANG("Xhosa", "xh", 0x0434),
LANG("Yiddish", "yi", 0x043D),
@@ -1815,15 +1792,13 @@
uint16_t get_usb_code_for_current_locale(void)
{
- char *locale = NULL;
+ char *locale;
char search_string[64];
char *ptr;
struct lang_map_entry *lang;
/* Get the current locale. */
-#ifdef HAVE_SETLOCALE
locale = setlocale(0, NULL);
-#endif
if (!locale)
return 0x0;
@@ -1878,10 +1853,6 @@
return 0x0;
}
-#if defined(__cplusplus) && !defined(NAMESPACE)
-}
-#endif
-
-#ifdef NAMESPACE
+#ifdef __cplusplus
}
#endif
diff --git a/src/hidapi/libusb/hidapi_libusb.h b/src/hidapi/libusb/hidapi_libusb.h
new file mode 100644
index 0000000..2920768
--- /dev/null
+++ b/src/hidapi/libusb/hidapi_libusb.h
@@ -0,0 +1,56 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ libusb/hidapi Team
+
+ Copyright 2021, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+ https://github.com/libusb/hidapi .
+********************************************************/
+
+/** @file
+ * @defgroup API hidapi API
+
+ * Since version 0.11.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 11, 0).
+ */
+
+#ifndef HIDAPI_LIBUSB_H__
+#define HIDAPI_LIBUSB_H__
+
+#include <stdint.h>
+
+#include "hidapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /** @brief Open a HID device using libusb_wrap_sys_device.
+ See https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html#ga98f783e115ceff4eaf88a60e6439563c,
+ for details on libusb_wrap_sys_device.
+
+ @ingroup API
+ @param sys_dev Platform-specific file descriptor that can be recognised by libusb.
+ @param interface_num USB interface number of the device to be used as HID interface.
+ Pass -1 to select first HID interface of the device.
+
+ @returns
+ This function returns a pointer to a #hid_device object on
+ success or NULL on failure.
+ */
+ HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys_dev, int interface_num);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/hidapi/libusb/hidusb.cpp b/src/hidapi/libusb/hidusb.cpp
deleted file mode 100644
index 5006306..0000000
--- a/src/hidapi/libusb/hidusb.cpp
+++ /dev/null
@@ -1,3 +0,0 @@
-
-#define NAMESPACE HIDUSB
-#include "hid.c"
diff --git a/src/hidapi/linux/.gitignore b/src/hidapi/linux/.gitignore
new file mode 100644
index 0000000..127bf37
--- /dev/null
+++ b/src/hidapi/linux/.gitignore
@@ -0,0 +1,18 @@
+Debug
+Release
+*.exp
+*.ilk
+*.lib
+*.suo
+*.vcproj.*
+*.ncb
+*.suo
+*.dll
+*.pdb
+*.o
+*.so
+hidtest-hidraw
+.deps
+.libs
+*.lo
+*.la
diff --git a/src/hidapi/linux/CMakeLists.txt b/src/hidapi/linux/CMakeLists.txt
new file mode 100644
index 0000000..0970ac3
--- /dev/null
+++ b/src/hidapi/linux/CMakeLists.txt
@@ -0,0 +1,38 @@
+cmake_minimum_required(VERSION 3.6.3 FATAL_ERROR)
+
+add_library(hidapi_hidraw
+ ${HIDAPI_PUBLIC_HEADERS}
+ hid.c
+)
+target_link_libraries(hidapi_hidraw PUBLIC hidapi_include)
+
+find_package(Threads REQUIRED)
+
+include(FindPkgConfig)
+pkg_check_modules(libudev REQUIRED IMPORTED_TARGET libudev)
+
+target_link_libraries(hidapi_hidraw PRIVATE PkgConfig::libudev Threads::Threads)
+
+set_target_properties(hidapi_hidraw
+ PROPERTIES
+ EXPORT_NAME "hidraw"
+ OUTPUT_NAME "hidapi-hidraw"
+ VERSION ${PROJECT_VERSION}
+ SOVERSION ${PROJECT_VERSION_MAJOR}
+ PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}"
+)
+
+# compatibility with find_package()
+add_library(hidapi::hidraw ALIAS hidapi_hidraw)
+# compatibility with raw library link
+add_library(hidapi-hidraw ALIAS hidapi_hidraw)
+
+if(HIDAPI_INSTALL_TARGETS)
+ install(TARGETS hidapi_hidraw EXPORT hidapi
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hidapi"
+ )
+endif()
+
+hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi-hidraw.pc.in")
diff --git a/src/hidapi/linux/Makefile-manual b/src/hidapi/linux/Makefile-manual
index 04ce1de..81d28cf 100644
--- a/src/hidapi/linux/Makefile-manual
+++ b/src/hidapi/linux/Makefile-manual
@@ -13,23 +13,19 @@
CC ?= gcc
CFLAGS ?= -Wall -g -fpic
-CXX ?= g++
-CXXFLAGS ?= -Wall -g -fpic
-
LDFLAGS ?= -Wall -g
-COBJS = hid.o
-CPPOBJS = ../hidtest/hidtest.o
-OBJS = $(COBJS) $(CPPOBJS)
+COBJS = hid.o ../hidtest/test.o
+OBJS = $(COBJS)
LIBS_UDEV = `pkg-config libudev --libs` -lrt
LIBS = $(LIBS_UDEV)
INCLUDES ?= -I../hidapi `pkg-config libusb-1.0 --cflags`
# Console Test Program
-hidtest-hidraw: $(COBJS) $(CPPOBJS)
- $(CXX) $(LDFLAGS) $^ $(LIBS_UDEV) -o $@
+hidtest-hidraw: $(COBJS)
+ $(CC) $(LDFLAGS) $^ $(LIBS_UDEV) -o $@
# Shared Libs
libhidapi-hidraw.so: $(COBJS)
@@ -39,11 +35,8 @@
$(COBJS): %.o: %.c
$(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@
-$(CPPOBJS): %.o: %.cpp
- $(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@
-
clean:
- rm -f $(OBJS) hidtest-hidraw libhidapi-hidraw.so ../hidtest/hidtest.o
+ rm -f $(OBJS) hidtest-hidraw libhidapi-hidraw.so $(COBJS)
.PHONY: clean libs
diff --git a/src/hidapi/linux/README.txt b/src/hidapi/linux/README.txt
deleted file mode 100644
index c187590..0000000
--- a/src/hidapi/linux/README.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-
-There are two implementations of HIDAPI for Linux. One (linux/hid.c) uses the
-Linux hidraw driver, and the other (libusb/hid.c) uses libusb. Which one you
-use depends on your application. Complete functionality of the hidraw
-version depends on patches to the Linux kernel which are not currently in
-the mainline. These patches have to do with sending and receiving feature
-reports. The libusb implementation uses libusb to talk directly to the
-device, bypassing any Linux HID driver. The disadvantage of the libusb
-version is that it will only work with USB devices, while the hidraw
-implementation will work with Bluetooth devices as well.
-
-To use HIDAPI, simply drop either linux/hid.c or libusb/hid.c into your
-application and build using the build parameters in the Makefile.
-
-
-Libusb Implementation notes
-----------------------------
-For the libusb implementation, libusb-1.0 must be installed. Libusb 1.0 is
-different than the legacy libusb 0.1 which is installed on many systems. To
-install libusb-1.0 on Ubuntu and other Debian-based systems, run:
- sudo apt-get install libusb-1.0-0-dev
-
-
-Hidraw Implementation notes
-----------------------------
-For the hidraw implementation, libudev headers and libraries are required to
-build hidapi programs. To install libudev libraries on Ubuntu,
-and other Debian-based systems, run:
- sudo apt-get install libudev-dev
-
-On Redhat-based systems, run the following as root:
- yum install libudev-devel
-
-Unfortunately, the hidraw driver, which the linux version of hidapi is based
-on, contains bugs in kernel versions < 2.6.36, which the client application
-should be aware of.
-
-Bugs (hidraw implementation only):
------------------------------------
-On Kernel versions < 2.6.34, if your device uses numbered reports, an extra
-byte will be returned at the beginning of all reports returned from read()
-for hidraw devices. This is worked around in the library. No action should be
-necessary in the client library.
-
-On Kernel versions < 2.6.35, reports will only be sent using a Set_Report
-transfer on the CONTROL endpoint. No data will ever be sent on an Interrupt
-Out endpoint if one exists. This is fixed in 2.6.35. In 2.6.35, OUTPUT
-reports will be sent to the device on the first INTERRUPT OUT endpoint if it
-exists; If it does not exist, OUTPUT reports will be sent on the CONTROL
-endpoint.
-
-On Kernel versions < 2.6.36, add an extra byte containing the report number
-to sent reports if numbered reports are used, and the device does not
-contain an INTERRPUT OUT endpoint for OUTPUT transfers. For example, if
-your device uses numbered reports and wants to send {0x2 0xff 0xff 0xff} to
-the device (0x2 is the report number), you must send {0x2 0x2 0xff 0xff
-0xff}. If your device has the optional Interrupt OUT endpoint, this does not
-apply (but really on 2.6.35 only, because 2.6.34 won't use the interrupt
-out endpoint).
diff --git a/src/hidapi/linux/hid.c b/src/hidapi/linux/hid.c
index ee8ccef..a499f04 100644
--- a/src/hidapi/linux/hid.c
+++ b/src/hidapi/linux/hid.c
@@ -5,10 +5,9 @@
Alan Ott
Signal 11 Software
- 8/22/2009
- Linux Version - 6/2/2009
+ libusb/hidapi Team
- Copyright 2009, All Rights Reserved.
+ Copyright 2022, All Rights Reserved.
At the discretion of the user of this library,
this software may be licensed under the terms of the
@@ -20,12 +19,6 @@
code repository located at:
https://github.com/libusb/hidapi .
********************************************************/
-#include "SDL_internal.h"
-
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */
-#endif
/* C */
#include <stdio.h>
@@ -49,15 +42,15 @@
#include <linux/input.h>
#include <libudev.h>
-#include "../hidapi/hidapi.h"
+#include "hidapi.h"
-#ifdef NAMESPACE
-namespace NAMESPACE
-{
-#endif
-
-/* Definitions from linux/hidraw.h. Since these are new, some distros
- may not have header files which contain them. */
+#ifdef HIDAPI_ALLOW_BUILD_WORKAROUND_KERNEL_2_6_39
+/* This definitions first appeared in Linux Kernel 2.6.39 in linux/hidraw.h.
+ hidapi doesn't support kernels older than that,
+ so we don't define macros below explicitly, to fail builds on old kernels.
+ For those who really need this as a workaround (e.g. to be able to build on old build machines),
+ can workaround by defining the macro above.
+*/
#ifndef HIDIOCSFEATURE
#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
#endif
@@ -65,60 +58,43 @@
#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
#endif
-/* USB HID device property names */
-const char *device_string_names[] = {
- "manufacturer",
- "product",
- "serial",
-};
+#endif
-/* Symbolic names for the properties above */
-enum device_string_id {
- DEVICE_STRING_MANUFACTURER,
- DEVICE_STRING_PRODUCT,
- DEVICE_STRING_SERIAL,
- DEVICE_STRING_COUNT,
-};
+// HIDIOCGINPUT is not defined in Linux kernel headers < 5.11.
+// This definition is from hidraw.h in Linux >= 5.11.
+// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f43d3870cafa2a0f3854c1819c8385733db8f9ae
+#ifndef HIDIOCGINPUT
+#define HIDIOCGINPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0A, len)
+#endif
struct hid_device_ {
int device_handle;
int blocking;
- int uses_numbered_reports;
- int needs_ble_hack;
+ wchar_t *last_error_str;
+ struct hid_device_info* device_info;
};
+static struct hid_api_version api_version = {
+ .major = HID_API_VERSION_MAJOR,
+ .minor = HID_API_VERSION_MINOR,
+ .patch = HID_API_VERSION_PATCH
+};
-static __u32 kernel_version = 0;
+static wchar_t *last_global_error_str = NULL;
-static __u32 detect_kernel_version(void)
-{
- struct utsname name;
- int major, minor, release;
- int ret;
-
- uname(&name);
- ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release);
- if (ret == 3) {
- return KERNEL_VERSION(major, minor, release);
- }
-
- ret = sscanf(name.release, "%d.%d", &major, &minor);
- if (ret == 2) {
- return KERNEL_VERSION(major, minor, 0);
- }
-
- printf("Couldn't determine kernel version from version string \"%s\"\n", name.release);
- return 0;
-}
static hid_device *new_hid_device(void)
{
- hid_device *dev = (hid_device *)calloc(1, sizeof(hid_device));
+ hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
+ if (dev == NULL) {
+ return NULL;
+ }
+
dev->device_handle = -1;
dev->blocking = 1;
- dev->uses_numbered_reports = 0;
- dev->needs_ble_hack = 0;
+ dev->last_error_str = NULL;
+ dev->device_info = NULL;
return dev;
}
@@ -134,7 +110,11 @@
if ((size_t) -1 == wlen) {
return wcsdup(L"");
}
- ret = (wchar_t *)calloc(wlen+1, sizeof(wchar_t));
+ ret = (wchar_t*) calloc(wlen+1, sizeof(wchar_t));
+ if (ret == NULL) {
+ /* as much as we can do at this point */
+ return NULL;
+ }
mbstowcs(ret, utf8, wlen+1);
ret[wlen] = 0x0000;
}
@@ -142,6 +122,64 @@
return ret;
}
+
+/* Makes a copy of the given error message (and decoded according to the
+ * currently locale) into the wide string pointer pointed by error_str.
+ * The last stored error string is freed.
+ * Use register_error_str(NULL) to free the error message completely. */
+static void register_error_str(wchar_t **error_str, const char *msg)
+{
+ free(*error_str);
+ *error_str = utf8_to_wchar_t(msg);
+}
+
+/* Semilar to register_error_str, but allows passing a format string with va_list args into this function. */
+static void register_error_str_vformat(wchar_t **error_str, const char *format, va_list args)
+{
+ char msg[256];
+ vsnprintf(msg, sizeof(msg), format, args);
+
+ register_error_str(error_str, msg);
+}
+
+/* Set the last global error to be reported by hid_error(NULL).
+ * The given error message will be copied (and decoded according to the
+ * currently locale, so do not pass in string constants).
+ * The last stored global error message is freed.
+ * Use register_global_error(NULL) to indicate "no error". */
+static void register_global_error(const char *msg)
+{
+ register_error_str(&last_global_error_str, msg);
+}
+
+/* Similar to register_global_error, but allows passing a format string into this function. */
+static void register_global_error_format(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ register_error_str_vformat(&last_global_error_str, format, args);
+ va_end(args);
+}
+
+/* Set the last error for a device to be reported by hid_error(dev).
+ * The given error message will be copied (and decoded according to the
+ * currently locale, so do not pass in string constants).
+ * The last stored device error message is freed.
+ * Use register_device_error(dev, NULL) to indicate "no error". */
+static void register_device_error(hid_device *dev, const char *msg)
+{
+ register_error_str(&dev->last_error_str, msg);
+}
+
+/* Similar to register_device_error, but you can pass a format string into this function. */
+static void register_device_error_format(hid_device *dev, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ register_error_str_vformat(&dev->last_error_str, format, args);
+ va_end(args);
+}
+
/* Get an attribute value from a udev_device and return it as a whar_t
string. The returned string must be freed with free() when done.*/
static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name)
@@ -149,78 +187,337 @@
return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name));
}
-/* uses_numbered_reports() returns 1 if report_descriptor describes a device
- which contains numbered reports. */
-static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) {
- unsigned int i = 0;
+/*
+ * Gets the size of the HID item at the given position
+ * Returns 1 if successful, 0 if an invalid key
+ * Sets data_len and key_size when successful
+ */
+static int get_hid_item_size(__u8 *report_descriptor, unsigned int pos, __u32 size, int *data_len, int *key_size)
+{
+ int key = report_descriptor[pos];
int size_code;
- int data_len, key_size;
- while (i < size) {
- int key = report_descriptor[i];
-
- /* Check for the Report ID key */
- if (key == 0x85/*Report ID*/) {
- /* This device has a Report ID, which means it uses
- numbered reports. */
+ /*
+ * This is a Long Item. The next byte contains the
+ * length of the data section (value) for this key.
+ * See the HID specification, version 1.11, section
+ * 6.2.2.3, titled "Long Items."
+ */
+ if ((key & 0xf0) == 0xf0) {
+ if (pos + 1 < size)
+ {
+ *data_len = report_descriptor[pos + 1];
+ *key_size = 3;
return 1;
}
-
- //printf("key: %02hhx\n", key);
-
- if ((key & 0xf0) == 0xf0) {
- /* This is a Long Item. The next byte contains the
- length of the data section (value) for this key.
- See the HID specification, version 1.11, section
- 6.2.2.3, titled "Long Items." */
- if (i+1 < size)
- data_len = report_descriptor[i+1];
- else
- data_len = 0; /* malformed report */
- key_size = 3;
- }
- else {
- /* This is a Short Item. The bottom two bits of the
- key contain the size code for the data section
- (value) for this key. Refer to the HID
- specification, version 1.11, section 6.2.2.2,
- titled "Short Items." */
- size_code = key & 0x3;
- switch (size_code) {
- case 0:
- case 1:
- case 2:
- data_len = size_code;
- break;
- case 3:
- data_len = 4;
- break;
- default:
- /* Can't ever happen since size_code is & 0x3 */
- data_len = 0;
- break;
- }
- key_size = 1;
- }
-
- /* Skip over this key and it's associated data */
- i += data_len + key_size;
+ *data_len = 0; /* malformed report */
+ *key_size = 0;
}
- /* Didn't find a Report ID key. Device doesn't use numbered reports. */
+ /*
+ * This is a Short Item. The bottom two bits of the
+ * key contain the size code for the data section
+ * (value) for this key. Refer to the HID
+ * specification, version 1.11, section 6.2.2.2,
+ * titled "Short Items."
+ */
+ size_code = key & 0x3;
+ switch (size_code) {
+ case 0:
+ case 1:
+ case 2:
+ *data_len = size_code;
+ *key_size = 1;
+ return 1;
+ case 3:
+ *data_len = 4;
+ *key_size = 1;
+ return 1;
+ default:
+ /* Can't ever happen since size_code is & 0x3 */
+ *data_len = 0;
+ *key_size = 0;
+ break;
+ };
+
+ /* malformed report */
return 0;
}
/*
+ * Get bytes from a HID Report Descriptor.
+ * Only call with a num_bytes of 0, 1, 2, or 4.
+ */
+static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_t cur)
+{
+ /* Return if there aren't enough bytes. */
+ if (cur + num_bytes >= len)
+ return 0;
+
+ if (num_bytes == 0)
+ return 0;
+ else if (num_bytes == 1)
+ return rpt[cur + 1];
+ else if (num_bytes == 2)
+ return (rpt[cur + 2] * 256 + rpt[cur + 1]);
+ else if (num_bytes == 4)
+ return (
+ rpt[cur + 4] * 0x01000000 +
+ rpt[cur + 3] * 0x00010000 +
+ rpt[cur + 2] * 0x00000100 +
+ rpt[cur + 1] * 0x00000001
+ );
+ else
+ return 0;
+}
+
+/*
+ * Retrieves the device's Usage Page and Usage from the report descriptor.
+ * The algorithm returns the current Usage Page/Usage pair whenever a new
+ * Collection is found and a Usage Local Item is currently in scope.
+ * Usage Local Items are consumed by each Main Item (See. 6.2.2.8).
+ * The algorithm should give similar results as Apple's:
+ * https://developer.apple.com/documentation/iokit/kiohiddeviceusagepairskey?language=objc
+ * Physical Collections are also matched (macOS does the same).
+ *
+ * This function can be called repeatedly until it returns non-0
+ * Usage is found. pos is the starting point (initially 0) and will be updated
+ * to the next search position.
+ *
+ * The return value is 0 when a pair is found.
+ * 1 when finished processing descriptor.
+ * -1 on a malformed report.
+ */
+static int get_next_hid_usage(__u8 *report_descriptor, __u32 size, unsigned int *pos, unsigned short *usage_page, unsigned short *usage)
+{
+ int data_len, key_size;
+ int initial = *pos == 0; /* Used to handle case where no top-level application collection is defined */
+ int usage_pair_ready = 0;
+
+ /* Usage is a Local Item, it must be set before each Main Item (Collection) before a pair is returned */
+ int usage_found = 0;
+
+ while (*pos < size) {
+ int key = report_descriptor[*pos];
+ int key_cmd = key & 0xfc;
+
+ /* Determine data_len and key_size */
+ if (!get_hid_item_size(report_descriptor, *pos, size, &data_len, &key_size))
+ return -1; /* malformed report */
+
+ switch (key_cmd) {
+ case 0x4: /* Usage Page 6.2.2.7 (Global) */
+ *usage_page = get_hid_report_bytes(report_descriptor, size, data_len, *pos);
+ break;
+
+ case 0x8: /* Usage 6.2.2.8 (Local) */
+ *usage = get_hid_report_bytes(report_descriptor, size, data_len, *pos);
+ usage_found = 1;
+ break;
+
+ case 0xa0: /* Collection 6.2.2.4 (Main) */
+ /* A Usage Item (Local) must be found for the pair to be valid */
+ if (usage_found)
+ usage_pair_ready = 1;
+
+ /* Usage is a Local Item, unset it */
+ usage_found = 0;
+ break;
+
+ case 0x80: /* Input 6.2.2.4 (Main) */
+ case 0x90: /* Output 6.2.2.4 (Main) */
+ case 0xb0: /* Feature 6.2.2.4 (Main) */
+ case 0xc0: /* End Collection 6.2.2.4 (Main) */
+ /* Usage is a Local Item, unset it */
+ usage_found = 0;
+ break;
+ }
+
+ /* Skip over this key and its associated data */
+ *pos += data_len + key_size;
+
+ /* Return usage pair */
+ if (usage_pair_ready)
+ return 0;
+ }
+
+ /* If no top-level application collection is found and usage page/usage pair is found, pair is valid
+ https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/top-level-collections */
+ if (initial && usage_found)
+ return 0; /* success */
+
+ return 1; /* finished processing */
+}
+
+/*
+ * Retrieves the hidraw report descriptor from a file.
+ * When using this form, <sysfs_path>/device/report_descriptor, elevated priviledges are not required.
+ */
+static int get_hid_report_descriptor(const char *rpt_path, struct hidraw_report_descriptor *rpt_desc)
+{
+ int rpt_handle;
+ ssize_t res;
+
+ rpt_handle = open(rpt_path, O_RDONLY | O_CLOEXEC);
+ if (rpt_handle < 0) {
+ register_global_error_format("open failed (%s): %s", rpt_path, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * Read in the Report Descriptor
+ * The sysfs file has a maximum size of 4096 (which is the same as HID_MAX_DESCRIPTOR_SIZE) so we should always
+ * be ok when reading the descriptor.
+ * In practice if the HID descriptor is any larger I suspect many other things will break.
+ */
+ memset(rpt_desc, 0x0, sizeof(*rpt_desc));
+ res = read(rpt_handle, rpt_desc->value, HID_MAX_DESCRIPTOR_SIZE);
+ if (res < 0) {
+ register_global_error_format("read failed (%s): %s", rpt_path, strerror(errno));
+ }
+ rpt_desc->size = (__u32) res;
+
+ close(rpt_handle);
+ return (int) res;
+}
+
+/* return size of the descriptor, or -1 on failure */
+static int get_hid_report_descriptor_from_sysfs(const char *sysfs_path, struct hidraw_report_descriptor *rpt_desc)
+{
+ int res = -1;
+ /* Construct <sysfs_path>/device/report_descriptor */
+ size_t rpt_path_len = strlen(sysfs_path) + 25 + 1;
+ char* rpt_path = (char*) calloc(1, rpt_path_len);
+ snprintf(rpt_path, rpt_path_len, "%s/device/report_descriptor", sysfs_path);
+
+ res = get_hid_report_descriptor(rpt_path, rpt_desc);
+ free(rpt_path);
+
+ return res;
+}
+
+/* return non-zero if successfully parsed */
+static int parse_hid_vid_pid_from_uevent(const char *uevent, unsigned *bus_type, unsigned short *vendor_id, unsigned short *product_id)
+{
+ char tmp[1024];
+ size_t uevent_len = strlen(uevent);
+ if (uevent_len > sizeof(tmp) - 1)
+ uevent_len = sizeof(tmp) - 1;
+ memcpy(tmp, uevent, uevent_len);
+ tmp[uevent_len] = '\0';
+
+ char *saveptr = NULL;
+ char *line;
+ char *key;
+ char *value;
+
+ line = strtok_r(tmp, "\n", &saveptr);
+ while (line != NULL) {
+ /* line: "KEY=value" */
+ key = line;
+ value = strchr(line, '=');
+ if (!value) {
+ goto next_line;
+ }
+ *value = '\0';
+ value++;
+
+ if (strcmp(key, "HID_ID") == 0) {
+ /**
+ * type vendor product
+ * HID_ID=0003:000005AC:00008242
+ **/
+ int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id);
+ if (ret == 3) {
+ return 1;
+ }
+ }
+
+next_line:
+ line = strtok_r(NULL, "\n", &saveptr);
+ }
+
+ register_global_error("Couldn't find/parse HID_ID");
+ return 0;
+}
+
+/* return non-zero if successfully parsed */
+static int parse_hid_vid_pid_from_uevent_path(const char *uevent_path, unsigned *bus_type, unsigned short *vendor_id, unsigned short *product_id)
+{
+ int handle;
+ ssize_t res;
+
+ handle = open(uevent_path, O_RDONLY | O_CLOEXEC);
+ if (handle < 0) {
+ register_global_error_format("open failed (%s): %s", uevent_path, strerror(errno));
+ return 0;
+ }
+
+ char buf[1024];
+ res = read(handle, buf, sizeof(buf) - 1); /* -1 for '\0' at the end */
+ close(handle);
+
+ if (res < 0) {
+ register_global_error_format("read failed (%s): %s", uevent_path, strerror(errno));
+ return 0;
+ }
+
+ buf[res] = '\0';
+ return parse_hid_vid_pid_from_uevent(buf, bus_type, vendor_id, product_id);
+}
+
+/* return non-zero if successfully read/parsed */
+static int parse_hid_vid_pid_from_sysfs(const char *sysfs_path, unsigned *bus_type, unsigned short *vendor_id, unsigned short *product_id)
+{
+ int res = 0;
+ /* Construct <sysfs_path>/device/uevent */
+ size_t uevent_path_len = strlen(sysfs_path) + 14 + 1;
+ char* uevent_path = (char*) calloc(1, uevent_path_len);
+ snprintf(uevent_path, uevent_path_len, "%s/device/uevent", sysfs_path);
+
+ res = parse_hid_vid_pid_from_uevent_path(uevent_path, bus_type, vendor_id, product_id);
+ free(uevent_path);
+
+ return res;
+}
+
+static int get_hid_report_descriptor_from_hidraw(hid_device *dev, struct hidraw_report_descriptor *rpt_desc)
+{
+ int desc_size = 0;
+
+ /* Get Report Descriptor Size */
+ int res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size);
+ if (res < 0) {
+ register_device_error_format(dev, "ioctl(GRDESCSIZE): %s", strerror(errno));
+ return res;
+ }
+
+ /* Get Report Descriptor */
+ memset(rpt_desc, 0x0, sizeof(*rpt_desc));
+ rpt_desc->size = desc_size;
+ res = ioctl(dev->device_handle, HIDIOCGRDESC, rpt_desc);
+ if (res < 0) {
+ register_device_error_format(dev, "ioctl(GRDESC): %s", strerror(errno));
+ }
+
+ return res;
+}
+
+/*
* The caller is responsible for free()ing the (newly-allocated) character
* strings pointed to by serial_number_utf8 and product_name_utf8 after use.
*/
-static int
-parse_uevent_info(const char *uevent, unsigned *bus_type,
+static int parse_uevent_info(const char *uevent, unsigned *bus_type,
unsigned short *vendor_id, unsigned short *product_id,
char **serial_number_utf8, char **product_name_utf8)
{
- char *tmp;
+ char tmp[1024];
+ size_t uevent_len = strlen(uevent);
+ if (uevent_len > sizeof(tmp) - 1)
+ uevent_len = sizeof(tmp) - 1;
+ memcpy(tmp, uevent, uevent_len);
+ tmp[uevent_len] = '\0';
+
char *saveptr = NULL;
char *line;
char *key;
@@ -230,15 +527,6 @@
int found_serial = 0;
int found_name = 0;
- if (!uevent) {
- return 0;
- }
-
- tmp = strdup(uevent);
- if (!tmp) {
- return 0;
- }
-
line = strtok_r(tmp, "\n", &saveptr);
while (line != NULL) {
/* line: "KEY=value" */
@@ -273,170 +561,203 @@
line = strtok_r(NULL, "\n", &saveptr);
}
- free(tmp);
return (found_id && found_name && found_serial);
}
-static int is_BLE(hid_device *dev)
+
+static struct hid_device_info * create_device_info_for_device(struct udev_device *raw_dev)
{
- struct udev *udev;
- struct udev_device *udev_dev, *hid_dev;
- struct stat s;
- int ret;
+ struct hid_device_info *root = NULL;
+ struct hid_device_info *cur_dev = NULL;
- /* Create the udev object */
- udev = udev_new();
- if (!udev) {
- printf("Can't create udev\n");
- return -1;
- }
-
- /* Get the dev_t (major/minor numbers) from the file handle. */
- if (fstat(dev->device_handle, &s) < 0) {
- udev_unref(udev);
- return -1;
- }
-
- /* Open a udev device from the dev_t. 'c' means character device. */
- ret = 0;
- udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev);
- if (udev_dev) {
- hid_dev = udev_device_get_parent_with_subsystem_devtype(
- udev_dev,
- "hid",
- NULL);
- if (hid_dev) {
- unsigned short dev_vid = 0;
- unsigned short dev_pid = 0;
- unsigned bus_type = 0;
- char *serial_number_utf8 = NULL;
- char *product_name_utf8 = NULL;
-
- parse_uevent_info(
- udev_device_get_sysattr_value(hid_dev, "uevent"),
- &bus_type,
- &dev_vid,
- &dev_pid,
- &serial_number_utf8,
- &product_name_utf8);
- free(serial_number_utf8);
- free(product_name_utf8);
-
- if (bus_type == BUS_BLUETOOTH) {
- /* Right now the Steam Controller is the only BLE device that we send feature reports to */
- if (dev_vid == 0x28de /* Valve */) {
- ret = 1;
- }
- }
-
- /* hid_dev doesn't need to be (and can't be) unref'd.
- I'm not sure why, but it'll throw double-free() errors. */
- }
- udev_device_unref(udev_dev);
- }
-
- udev_unref(udev);
-
- return ret;
-}
-
-static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen)
-{
- struct udev *udev;
- struct udev_device *udev_dev, *parent, *hid_dev;
- struct stat s;
- int ret = -1;
+ const char *sysfs_path;
+ const char *dev_path;
+ const char *str;
+ struct udev_device *hid_dev; /* The device's HID udev node. */
+ struct udev_device *usb_dev; /* The device's USB udev node. */
+ struct udev_device *intf_dev; /* The device's interface (in the USB sense). */
+ unsigned short dev_vid;
+ unsigned short dev_pid;
char *serial_number_utf8 = NULL;
char *product_name_utf8 = NULL;
- char *tmp;
+ unsigned bus_type;
+ int result;
+ struct hidraw_report_descriptor report_desc;
- /* Create the udev object */
- udev = udev_new();
- if (!udev) {
- printf("Can't create udev\n");
- return -1;
+ sysfs_path = udev_device_get_syspath(raw_dev);
+ dev_path = udev_device_get_devnode(raw_dev);
+
+ hid_dev = udev_device_get_parent_with_subsystem_devtype(
+ raw_dev,
+ "hid",
+ NULL);
+
+ if (!hid_dev) {
+ /* Unable to find parent hid device. */
+ goto end;
}
- /* Get the dev_t (major/minor numbers) from the file handle. */
- ret = fstat(dev->device_handle, &s);
- if (-1 == ret) {
- udev_unref(udev);
- return ret;
+ result = parse_uevent_info(
+ udev_device_get_sysattr_value(hid_dev, "uevent"),
+ &bus_type,
+ &dev_vid,
+ &dev_pid,
+ &serial_number_utf8,
+ &product_name_utf8);
+
+ if (!result) {
+ /* parse_uevent_info() failed for at least one field. */
+ goto end;
}
- /* Open a udev device from the dev_t. 'c' means character device. */
- udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev);
- if (udev_dev) {
- hid_dev = udev_device_get_parent_with_subsystem_devtype(
- udev_dev,
- "hid",
- NULL);
- if (hid_dev) {
- unsigned short dev_vid;
- unsigned short dev_pid;
- unsigned bus_type;
- size_t retm;
- ret = parse_uevent_info(
- udev_device_get_sysattr_value(hid_dev, "uevent"),
- &bus_type,
- &dev_vid,
- &dev_pid,
- &serial_number_utf8,
- &product_name_utf8);
+ /* Filter out unhandled devices right away */
+ switch (bus_type) {
+ case BUS_BLUETOOTH:
+ case BUS_I2C:
+ case BUS_USB:
+ case BUS_SPI:
+ break;
- if (bus_type == BUS_BLUETOOTH) {
- switch (key) {
- case DEVICE_STRING_MANUFACTURER:
- wcsncpy(string, L"", maxlen);
- ret = 0;
- break;
- case DEVICE_STRING_PRODUCT:
- retm = mbstowcs(string, product_name_utf8, maxlen);
- ret = (retm == (size_t)-1)? -1: 0;
- break;
- case DEVICE_STRING_SERIAL:
- /* Bluetooth serial numbers are often the bluetooth device address
- and we want that with the colons stripped out, which is the correct
- serial number for PS4 controllers
- */
- while ((tmp = strchr(serial_number_utf8, ':')) != NULL) {
- memmove(tmp, tmp+1, strlen(tmp));
- }
- retm = mbstowcs(string, serial_number_utf8, maxlen);
- ret = (retm == (size_t)-1)? -1: 0;
- break;
- case DEVICE_STRING_COUNT:
- default:
- ret = -1;
- break;
- }
+ default:
+ goto end;
+ }
+
+ /* Create the record. */
+ root = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
+ if (!root)
+ goto end;
+
+ cur_dev = root;
+
+ /* Fill out the record */
+ cur_dev->next = NULL;
+ cur_dev->path = dev_path? strdup(dev_path): NULL;
+
+ /* VID/PID */
+ cur_dev->vendor_id = dev_vid;
+ cur_dev->product_id = dev_pid;
+
+ /* Serial Number */
+ cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8);
+
+ /* Release Number */
+ cur_dev->release_number = 0x0;
+
+ /* Interface Number */
+ cur_dev->interface_number = -1;
+
+ switch (bus_type) {
+ case BUS_USB:
+ /* The device pointed to by raw_dev contains information about
+ the hidraw device. In order to get information about the
+ USB device, get the parent device with the
+ subsystem/devtype pair of "usb"/"usb_device". This will
+ be several levels up the tree, but the function will find
+ it. */
+ usb_dev = udev_device_get_parent_with_subsystem_devtype(
+ raw_dev,
+ "usb",
+ "usb_device");
+
+ /* uhid USB devices
+ * Since this is a virtual hid interface, no USB information will
+ * be available. */
+ if (!usb_dev) {
+ /* Manufacturer and Product strings */
+ cur_dev->manufacturer_string = wcsdup(L"");
+ cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);
+ break;
}
- else {
- /* This is a USB device. Find its parent USB Device node. */
- parent = udev_device_get_parent_with_subsystem_devtype(
- udev_dev,
- "usb",
- "usb_device");
- if (parent) {
- const char *str;
- const char *key_str = NULL;
- if ((int)key >= 0 && (int)key < DEVICE_STRING_COUNT) {
- key_str = device_string_names[key];
- } else {
- ret = -1;
- goto end;
- }
+ cur_dev->manufacturer_string = copy_udev_string(usb_dev, "manufacturer");
+ cur_dev->product_string = copy_udev_string(usb_dev, "product");
- str = udev_device_get_sysattr_value(parent, key_str);
- if (str) {
- /* Convert the string from UTF-8 to wchar_t */
- retm = mbstowcs(string, str, maxlen);
- ret = (retm == (size_t)-1)? -1: 0;
- goto end;
- }
- }
+ cur_dev->bus_type = HID_API_BUS_USB;
+
+ str = udev_device_get_sysattr_value(usb_dev, "bcdDevice");
+ cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0;
+
+ /* Get a handle to the interface's udev node. */
+ intf_dev = udev_device_get_parent_with_subsystem_devtype(
+ raw_dev,
+ "usb",
+ "usb_interface");
+ if (intf_dev) {
+ str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber");
+ cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1;
}
+
+ break;
+
+ case BUS_BLUETOOTH:
+ cur_dev->manufacturer_string = wcsdup(L"");
+ cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);
+
+ cur_dev->bus_type = HID_API_BUS_BLUETOOTH;
+
+ break;
+ case BUS_I2C:
+ cur_dev->manufacturer_string = wcsdup(L"");
+ cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);
+
+ cur_dev->bus_type = HID_API_BUS_I2C;
+
+ break;
+
+ case BUS_SPI:
+ cur_dev->manufacturer_string = wcsdup(L"");
+ cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);
+
+ cur_dev->bus_type = HID_API_BUS_SPI;
+
+ break;
+
+ default:
+ /* Unknown device type - this should never happen, as we
+ * check for USB and Bluetooth devices above */
+ break;
+ }
+
+ /* Usage Page and Usage */
+ result = get_hid_report_descriptor_from_sysfs(sysfs_path, &report_desc);
+ if (result >= 0) {
+ unsigned short page = 0, usage = 0;
+ unsigned int pos = 0;
+ /*
+ * Parse the first usage and usage page
+ * out of the report descriptor.
+ */
+ if (!get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage)) {
+ cur_dev->usage_page = page;
+ cur_dev->usage = usage;
+ }
+
+ /*
+ * Parse any additional usage and usage pages
+ * out of the report descriptor.
+ */
+ while (!get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage)) {
+ /* Create new record for additional usage pairs */
+ struct hid_device_info *tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
+ struct hid_device_info *prev_dev = cur_dev;
+
+ if (!tmp)
+ continue;
+ cur_dev->next = tmp;
+ cur_dev = tmp;
+
+ /* Update fields */
+ cur_dev->path = dev_path? strdup(dev_path): NULL;
+ cur_dev->vendor_id = dev_vid;
+ cur_dev->product_id = dev_pid;
+ cur_dev->serial_number = prev_dev->serial_number? wcsdup(prev_dev->serial_number): NULL;
+ cur_dev->release_number = prev_dev->release_number;
+ cur_dev->interface_number = prev_dev->interface_number;
+ cur_dev->manufacturer_string = prev_dev->manufacturer_string? wcsdup(prev_dev->manufacturer_string): NULL;
+ cur_dev->product_string = prev_dev->product_string? wcsdup(prev_dev->product_string): NULL;
+ cur_dev->usage_page = page;
+ cur_dev->usage = usage;
+ cur_dev->bus_type = prev_dev->bus_type;
}
}
@@ -444,35 +765,82 @@
free(serial_number_utf8);
free(product_name_utf8);
+ return root;
+}
+
+static struct hid_device_info * create_device_info_for_hid_device(hid_device *dev) {
+ struct udev *udev;
+ struct udev_device *udev_dev;
+ struct stat s;
+ int ret = -1;
+ struct hid_device_info *root = NULL;
+
+ register_device_error(dev, NULL);
+
+ /* Get the dev_t (major/minor numbers) from the file handle. */
+ ret = fstat(dev->device_handle, &s);
+ if (-1 == ret) {
+ register_device_error(dev, "Failed to stat device handle");
+ return NULL;
+ }
+
+ /* Create the udev object */
+ udev = udev_new();
+ if (!udev) {
+ register_device_error(dev, "Couldn't create udev context");
+ return NULL;
+ }
+
+ /* Open a udev device from the dev_t. 'c' means character device. */
+ udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev);
+ if (udev_dev) {
+ root = create_device_info_for_device(udev_dev);
+ }
+
+ if (!root) {
+ /* TODO: have a better error reporting via create_device_info_for_device */
+ register_device_error(dev, "Couldn't create hid_device_info");
+ }
+
udev_device_unref(udev_dev);
- /* parent and hid_dev don't need to be (and can't be) unref'd.
- I'm not sure why, but they'll throw double-free() errors. */
udev_unref(udev);
- return ret;
+ return root;
+}
+
+HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void)
+{
+ return &api_version;
+}
+
+HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
+{
+ return HID_API_VERSION_STR;
}
int HID_API_EXPORT hid_init(void)
{
const char *locale;
+ /* indicate no error */
+ register_global_error(NULL);
+
/* Set the locale if it's not set. */
locale = setlocale(LC_CTYPE, NULL);
if (!locale)
setlocale(LC_CTYPE, "");
- kernel_version = detect_kernel_version();
-
return 0;
}
int HID_API_EXPORT hid_exit(void)
{
- /* Nothing to do for this in the Linux/hidraw implementation. */
+ /* Free global error message */
+ register_global_error(NULL);
+
return 0;
}
-
struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
{
struct udev *udev;
@@ -481,15 +849,14 @@
struct hid_device_info *root = NULL; /* return object */
struct hid_device_info *cur_dev = NULL;
- struct hid_device_info *prev_dev = NULL; /* previous device */
- const char *hint = SDL_GetHint(SDL_HINT_HIDAPI_IGNORE_DEVICES);
hid_init();
+ /* register_global_error: global error is reset by hid_init */
/* Create the udev object */
udev = udev_new();
if (!udev) {
- printf("Can't create udev\n");
+ register_global_error("Couldn't create udev context");
return NULL;
}
@@ -502,178 +869,62 @@
create a udev_device record for it */
udev_list_entry_foreach(dev_list_entry, devices) {
const char *sysfs_path;
- const char *dev_path;
- const char *str;
+ unsigned short dev_vid = 0;
+ unsigned short dev_pid = 0;
+ unsigned bus_type = 0;
struct udev_device *raw_dev; /* The device's hidraw udev node. */
- struct udev_device *hid_dev; /* The device's HID udev node. */
- struct udev_device *usb_dev; /* The device's USB udev node. */
- struct udev_device *intf_dev; /* The device's interface (in the USB sense). */
- unsigned short dev_vid;
- unsigned short dev_pid;
- char *serial_number_utf8 = NULL;
- char *product_name_utf8 = NULL;
- unsigned bus_type;
- int result;
+ struct hid_device_info * tmp;
/* Get the filename of the /sys entry for the device
and create a udev_device object (dev) representing it */
sysfs_path = udev_list_entry_get_name(dev_list_entry);
- raw_dev = udev_device_new_from_syspath(udev, sysfs_path);
- dev_path = udev_device_get_devnode(raw_dev);
+ if (!sysfs_path)
+ continue;
- hid_dev = udev_device_get_parent_with_subsystem_devtype(
- raw_dev,
- "hid",
- NULL);
-
- if (!hid_dev) {
- /* Unable to find parent hid device. */
- goto next;
- }
-
- result = parse_uevent_info(
- udev_device_get_sysattr_value(hid_dev, "uevent"),
- &bus_type,
- &dev_vid,
- &dev_pid,
- &serial_number_utf8,
- &product_name_utf8);
-
- if (!result) {
- /* parse_uevent_info() failed for at least one field. */
- goto next;
- }
-
- if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) {
- /* We only know how to handle USB and BT devices. */
- goto next;
- }
-
- if (access(dev_path, R_OK|W_OK) != 0) {
- /* We can't open this device, ignore it */
- goto next;
- }
-
- /* See if there are any devices we should skip in enumeration */
- if (hint) {
- char vendor_match[16], product_match[16];
- SDL_snprintf(vendor_match, sizeof(vendor_match), "0x%.4x/0x0000", dev_vid);
- SDL_snprintf(product_match, sizeof(product_match), "0x%.4x/0x%.4x", dev_vid, dev_pid);
- if (SDL_strcasestr(hint, vendor_match) || SDL_strcasestr(hint, product_match)) {
+ if (vendor_id != 0 || product_id != 0) {
+ if (!parse_hid_vid_pid_from_sysfs(sysfs_path, &bus_type, &dev_vid, &dev_pid))
continue;
- }
+
+ if (vendor_id != 0 && vendor_id != dev_vid)
+ continue;
+ if (product_id != 0 && product_id != dev_pid)
+ continue;
}
- /* Check the VID/PID against the arguments */
- if ((vendor_id == 0x0 || vendor_id == dev_vid) &&
- (product_id == 0x0 || product_id == dev_pid)) {
- struct hid_device_info *tmp;
+ raw_dev = udev_device_new_from_syspath(udev, sysfs_path);
+ if (!raw_dev)
+ continue;
- /* VID/PID match. Create the record. */
- tmp = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info));
+ tmp = create_device_info_for_device(raw_dev);
+ if (tmp) {
if (cur_dev) {
cur_dev->next = tmp;
}
else {
root = tmp;
}
- prev_dev = cur_dev;
cur_dev = tmp;
- /* Fill out the record */
- cur_dev->next = NULL;
- cur_dev->path = dev_path? strdup(dev_path): NULL;
-
- /* VID/PID */
- cur_dev->vendor_id = dev_vid;
- cur_dev->product_id = dev_pid;
-
- /* Serial Number */
- cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8);
-
- /* Release Number */
- cur_dev->release_number = 0x0;
-
- /* Interface Number */
- cur_dev->interface_number = -1;
-
- switch (bus_type) {
- case BUS_USB:
- /* The device pointed to by raw_dev contains information about
- the hidraw device. In order to get information about the
- USB device, get the parent device with the
- subsystem/devtype pair of "usb"/"usb_device". This will
- be several levels up the tree, but the function will find
- it. */
- usb_dev = udev_device_get_parent_with_subsystem_devtype(
- raw_dev,
- "usb",
- "usb_device");
-
- if (!usb_dev) {
- /* Free this device */
- free(cur_dev->serial_number);
- free(cur_dev->path);
- free(cur_dev);
-
- /* Take it off the device list. */
- if (prev_dev) {
- prev_dev->next = NULL;
- cur_dev = prev_dev;
- }
- else {
- cur_dev = root = NULL;
- }
-
- goto next;
- }
-
- /* Manufacturer and Product strings */
- cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]);
- cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]);
-
- /* Release Number */
- str = udev_device_get_sysattr_value(usb_dev, "bcdDevice");
- cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0;
-
- /* Get a handle to the interface's udev node. */
- intf_dev = udev_device_get_parent_with_subsystem_devtype(
- raw_dev,
- "usb",
- "usb_interface");
- if (intf_dev) {
- str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber");
- cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1;
- }
-
- break;
-
- case BUS_BLUETOOTH:
- /* Manufacturer and Product strings */
- cur_dev->manufacturer_string = wcsdup(L"");
- cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);
-
- break;
-
- default:
- /* Unknown device type - this should never happen, as we
- * check for USB and Bluetooth devices above */
- break;
+ /* move the pointer to the tail of returnd list */
+ while (cur_dev->next != NULL) {
+ cur_dev = cur_dev->next;
}
}
- next:
- free(serial_number_utf8);
- free(product_name_utf8);
udev_device_unref(raw_dev);
- /* hid_dev, usb_dev and intf_dev don't need to be (and can't be)
- unref()d. It will cause a double-free() error. I'm not
- sure why. */
}
/* Free the enumerator and udev objects. */
udev_enumerate_unref(enumerate);
udev_unref(udev);
+ if (root == NULL) {
+ if (vendor_id == 0 && product_id == 0) {
+ register_global_error("No HID devices found in the system.");
+ } else {
+ register_global_error("No HID devices with requested VID/PID found in the system.");
+ }
+ }
+
return root;
}
@@ -697,7 +948,13 @@
const char *path_to_open = NULL;
hid_device *handle = NULL;
+ /* register_global_error: global error is reset by hid_enumerate/hid_init */
devs = hid_enumerate(vendor_id, product_id);
+ if (devs == NULL) {
+ /* register_global_error: global error is already set by hid_enumerate */
+ return NULL;
+ }
+
cur_dev = devs;
while (cur_dev) {
if (cur_dev->vendor_id == vendor_id &&
@@ -718,7 +975,9 @@
if (path_to_open) {
/* Open the device */
- handle = hid_open_path(path_to_open, 0);
+ handle = hid_open_path(path_to_open);
+ } else {
+ register_global_error("Device with requested VID/PID/(SerialNumber) not found");
}
hid_free_enumeration(devs);
@@ -726,51 +985,38 @@
return handle;
}
-hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive)
+hid_device * HID_API_EXPORT hid_open_path(const char *path)
{
hid_device *dev = NULL;
hid_init();
+ /* register_global_error: global error is reset by hid_init */
dev = new_hid_device();
+ if (!dev) {
+ register_global_error("Couldn't allocate memory");
+ return NULL;
+ }
- /* OPEN HERE */
dev->device_handle = open(path, O_RDWR | O_CLOEXEC);
- /* If we have a good handle, return it. */
if (dev->device_handle >= 0) {
-
- /* Get the report descriptor */
int res, desc_size = 0;
- struct hidraw_report_descriptor rpt_desc;
- memset(&rpt_desc, 0x0, sizeof(rpt_desc));
-
- /* Get Report Descriptor Size */
+ /* Make sure this is a HIDRAW device - responds to HIDIOCGRDESCSIZE */
res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size);
- if (res < 0)
- perror("HIDIOCGRDESCSIZE");
-
-
- /* Get Report Descriptor */
- rpt_desc.size = desc_size;
- res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc);
if (res < 0) {
- perror("HIDIOCGRDESC");
- } else {
- /* Determine if this device uses numbered reports. */
- dev->uses_numbered_reports =
- uses_numbered_reports(rpt_desc.value,
- rpt_desc.size);
+ hid_close(dev);
+ register_global_error_format("ioctl(GRDESCSIZE) error for '%s', not a HIDRAW device?: %s", path, strerror(errno));
+ return NULL;
}
- dev->needs_ble_hack = (is_BLE(dev) == 1);
-
return dev;
}
else {
- /* Unable to open any devices. */
+ /* Unable to open a device. */
free(dev);
+ register_global_error_format("Failed to open a device with path '%s': %s", path, strerror(errno));
return NULL;
}
}
@@ -780,14 +1026,25 @@
{
int bytes_written;
+ if (!data || (length == 0)) {
+ errno = EINVAL;
+ register_device_error(dev, strerror(errno));
+ return -1;
+ }
+
bytes_written = write(dev->device_handle, data, length);
+ register_device_error(dev, (bytes_written == -1)? strerror(errno): NULL);
+
return bytes_written;
}
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
{
+ /* Set device error to none */
+ register_device_error(dev, NULL);
+
int bytes_read;
if (milliseconds >= 0) {
@@ -804,29 +1061,32 @@
fds.events = POLLIN;
fds.revents = 0;
ret = poll(&fds, 1, milliseconds);
- if (ret == -1 || ret == 0) {
- /* Error or timeout */
+ if (ret == 0) {
+ /* Timeout */
+ return ret;
+ }
+ if (ret == -1) {
+ /* Error */
+ register_device_error(dev, strerror(errno));
return ret;
}
else {
/* Check for errors on the file descriptor. This will
indicate a device disconnection. */
- if (fds.revents & (POLLERR | POLLHUP | POLLNVAL))
+ if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) {
+ // We cannot use strerror() here as no -1 was returned from poll().
+ register_device_error(dev, "hid_read_timeout: unexpected poll error (device disconnected)");
return -1;
+ }
}
}
bytes_read = read(dev->device_handle, data, length);
- if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS))
- bytes_read = 0;
-
- if (bytes_read >= 0 &&
- kernel_version != 0 &&
- kernel_version < KERNEL_VERSION(2,6,34) &&
- dev->uses_numbered_reports) {
- /* Work around a kernel bug. Chop off the first byte. */
- memmove(data, data+1, bytes_read);
- bytes_read--;
+ if (bytes_read < 0) {
+ if (errno == EAGAIN || errno == EINPROGRESS)
+ bytes_read = 0;
+ else
+ register_device_error(dev, strerror(errno));
}
return bytes_read;
@@ -847,90 +1107,186 @@
return 0; /* Success */
}
+
int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
{
- static const int MAX_RETRIES = 50;
- int retry;
int res;
- for (retry = 0; retry < MAX_RETRIES; ++retry) {
- res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data);
- if (res < 0 && errno == EPIPE) {
- /* Try again... */
- continue;
- }
+ register_device_error(dev, NULL);
- if (res < 0)
- perror("ioctl (SFEATURE)");
- break;
- }
+ res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data);
+ if (res < 0)
+ register_device_error_format(dev, "ioctl (SFEATURE): %s", strerror(errno));
+
return res;
}
int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
{
int res;
- unsigned char report = data[0];
+
+ register_device_error(dev, NULL);
res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data);
if (res < 0)
- perror("ioctl (GFEATURE)");
- else if (dev->needs_ble_hack) {
- /* Versions of BlueZ before 5.56 don't include the report in the data,
- * and versions of BlueZ >= 5.56 include 2 copies of the report.
- * We'll fix it so that there is a single copy of the report in both cases
- */
- if (data[0] == report && data[1] == report) {
- memmove(&data[0], &data[1], res);
- } else if (data[0] != report) {
- memmove(&data[1], &data[0], res);
- data[0] = report;
- ++res;
- }
- }
+ register_device_error_format(dev, "ioctl (GFEATURE): %s", strerror(errno));
+
return res;
}
+int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
+{
+ int res;
+
+ register_device_error(dev, NULL);
+
+ res = ioctl(dev->device_handle, HIDIOCGINPUT(length), data);
+ if (res < 0)
+ register_device_error_format(dev, "ioctl (GINPUT): %s", strerror(errno));
+
+ return res;
+}
void HID_API_EXPORT hid_close(hid_device *dev)
{
if (!dev)
return;
+
close(dev->device_handle);
+
+ /* Free the device error message */
+ register_device_error(dev, NULL);
+
+ hid_free_enumeration(dev->device_info);
+
free(dev);
}
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
- return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen);
+ if (!string || !maxlen) {
+ register_device_error(dev, "Zero buffer/length");
+ return -1;
+ }
+
+ struct hid_device_info *info = hid_get_device_info(dev);
+ if (!info) {
+ // hid_get_device_info will have set an error already
+ return -1;
+ }
+
+ if (info->manufacturer_string) {
+ wcsncpy(string, info->manufacturer_string, maxlen);
+ string[maxlen - 1] = L'\0';
+ }
+ else {
+ string[0] = L'\0';
+ }
+
+ return 0;
}
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
- return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen);
+ if (!string || !maxlen) {
+ register_device_error(dev, "Zero buffer/length");
+ return -1;
+ }
+
+ struct hid_device_info *info = hid_get_device_info(dev);
+ if (!info) {
+ // hid_get_device_info will have set an error already
+ return -1;
+ }
+
+ if (info->product_string) {
+ wcsncpy(string, info->product_string, maxlen);
+ string[maxlen - 1] = L'\0';
+ }
+ else {
+ string[0] = L'\0';
+ }
+
+ return 0;
}
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
- return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen);
+ if (!string || !maxlen) {
+ register_device_error(dev, "Zero buffer/length");
+ return -1;
+ }
+
+ struct hid_device_info *info = hid_get_device_info(dev);
+ if (!info) {
+ // hid_get_device_info will have set an error already
+ return -1;
+ }
+
+ if (info->serial_number) {
+ wcsncpy(string, info->serial_number, maxlen);
+ string[maxlen - 1] = L'\0';
+ }
+ else {
+ string[0] = L'\0';
+ }
+
+ return 0;
+}
+
+
+HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev) {
+ if (!dev->device_info) {
+ // Lazy initialize device_info
+ dev->device_info = create_device_info_for_hid_device(dev);
+ }
+
+ // create_device_info_for_hid_device will set an error if needed
+ return dev->device_info;
}
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
{
- (void)dev;
(void)string_index;
(void)string;
(void)maxlen;
+
+ register_device_error(dev, "hid_get_indexed_string: not supported by hidraw");
+
return -1;
}
-HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
+int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
{
- return NULL;
+ struct hidraw_report_descriptor rpt_desc;
+ int res = get_hid_report_descriptor_from_hidraw(dev, &rpt_desc);
+ if (res < 0) {
+ /* error already registered */
+ return res;
+ }
+
+ if (rpt_desc.size < buf_size) {
+ buf_size = (size_t) rpt_desc.size;
+ }
+
+ memcpy(buf, rpt_desc.value, buf_size);
+
+ return (int) buf_size;
}
-#ifdef NAMESPACE
+
+/* Passing in NULL means asking for the last global error message. */
+HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
+{
+ if (dev) {
+ if (dev->last_error_str == NULL)
+ return L"Success";
+ return dev->last_error_str;
+ }
+
+ if (last_global_error_str == NULL)
+ return L"Success";
+ return last_global_error_str;
}
-#endif
diff --git a/src/hidapi/linux/hidraw.cpp b/src/hidapi/linux/hidraw.cpp
deleted file mode 100644
index 1bf6fad..0000000
--- a/src/hidapi/linux/hidraw.cpp
+++ /dev/null
@@ -1,3 +0,0 @@
-
-#define NAMESPACE HIDRAW
-#include "hid.c"
diff --git a/src/hidapi/m4/.gitignore b/src/hidapi/m4/.gitignore
new file mode 100644
index 0000000..8f79b02
--- /dev/null
+++ b/src/hidapi/m4/.gitignore
@@ -0,0 +1,5 @@
+# Ignore All, except pkg.m4, and of course this file.
+*
+!.gitignore
+!pkg.m4
+!ax_pthread.m4
diff --git a/src/hidapi/mac/.gitignore b/src/hidapi/mac/.gitignore
new file mode 100644
index 0000000..7cc3f0d
--- /dev/null
+++ b/src/hidapi/mac/.gitignore
@@ -0,0 +1,17 @@
+Debug
+Release
+*.exp
+*.ilk
+*.lib
+*.suo
+*.vcproj.*
+*.ncb
+*.suo
+*.dll
+*.pdb
+*.o
+hidapi-hidtest
+.deps
+.libs
+*.la
+*.lo
diff --git a/src/hidapi/mac/CMakeLists.txt b/src/hidapi/mac/CMakeLists.txt
new file mode 100644
index 0000000..a83664a
--- /dev/null
+++ b/src/hidapi/mac/CMakeLists.txt
@@ -0,0 +1,48 @@
+cmake_minimum_required(VERSION 3.4.3 FATAL_ERROR)
+
+list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_darwin.h")
+
+add_library(hidapi_darwin
+ ${HIDAPI_PUBLIC_HEADERS}
+ hid.c
+)
+
+find_package(Threads REQUIRED)
+
+target_link_libraries(hidapi_darwin
+ PUBLIC hidapi_include
+ PRIVATE Threads::Threads
+ PRIVATE "-framework IOKit" "-framework CoreFoundation" "-framework AppKit"
+)
+
+set_target_properties(hidapi_darwin
+ PROPERTIES
+ EXPORT_NAME "darwin"
+ OUTPUT_NAME "hidapi"
+ VERSION ${PROJECT_VERSION}
+ SOVERSION ${PROJECT_VERSION_MAJOR}
+ MACHO_COMPATIBILITY_VERSION ${PROJECT_VERSION_MAJOR}
+ FRAMEWORK_VERSION ${PROJECT_VERSION_MAJOR}
+ PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}"
+)
+
+# compatibility with find_package()
+add_library(hidapi::darwin ALIAS hidapi_darwin)
+# compatibility with raw library link
+add_library(hidapi ALIAS hidapi_darwin)
+
+set(PUBLIC_HEADER_DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
+if(NOT CMAKE_FRAMEWORK)
+ set(PUBLIC_HEADER_DESTINATION "${PUBLIC_HEADER_DESTINATION}/hidapi")
+endif()
+
+if(HIDAPI_INSTALL_TARGETS)
+ install(TARGETS hidapi_darwin EXPORT hidapi
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ FRAMEWORK DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ PUBLIC_HEADER DESTINATION "${PUBLIC_HEADER_DESTINATION}"
+ )
+endif()
+
+hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi.pc.in")
diff --git a/src/hidapi/mac/Makefile-manual b/src/hidapi/mac/Makefile-manual
index 5399b5a..2708492 100644
--- a/src/hidapi/mac/Makefile-manual
+++ b/src/hidapi/mac/Makefile-manual
@@ -9,24 +9,19 @@
all: hidtest
CC=gcc
-CXX=g++
-COBJS=hid.o
-CPPOBJS=../hidtest/hidtest.o
-OBJS=$(COBJS) $(CPPOBJS)
-CFLAGS+=-I../hidapi -Wall -g -c
-LIBS=-framework IOKit -framework CoreFoundation
+COBJS=hid.o ../hidtest/test.o
+OBJS=$(COBJS)
+CFLAGS+=-I../hidapi -I. -Wall -g -c
+LIBS=-framework IOKit -framework CoreFoundation -framework AppKit
hidtest: $(OBJS)
- g++ -Wall -g $^ $(LIBS) -o hidtest
+ $(CC) -Wall -g $^ $(LIBS) -o hidtest
$(COBJS): %.o: %.c
$(CC) $(CFLAGS) $< -o $@
-$(CPPOBJS): %.o: %.cpp
- $(CXX) $(CFLAGS) $< -o $@
-
clean:
- rm -f *.o hidtest $(CPPOBJS)
+ rm -f *.o hidtest
.PHONY: clean
diff --git a/src/hidapi/mac/hid.c b/src/hidapi/mac/hid.c
index 9004960..5c0914a 100644
--- a/src/hidapi/mac/hid.c
+++ b/src/hidapi/mac/hid.c
@@ -1,60 +1,67 @@
/*******************************************************
HIDAPI - Multi-Platform library for
communication with HID devices.
-
+
Alan Ott
Signal 11 Software
-
- 2010-07-03
-
- Copyright 2010, All Rights Reserved.
-
+
+ libusb/hidapi Team
+
+ Copyright 2022, All Rights Reserved.
+
At the discretion of the user of this library,
this software may be licensed under the terms of the
- GNU Public License v3, a BSD-Style license, or the
+ GNU General Public License v3, a BSD-Style license, or the
original HIDAPI license as outlined in the LICENSE.txt,
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
files located at the root of the source distribution.
These files may also be found in the public source
code repository located at:
- https://github.com/libusb/hidapi .
- ********************************************************/
-#include "SDL_internal.h"
-
+ https://github.com/libusb/hidapi .
+********************************************************/
/* See Apple Technical Note TN2187 for details on IOHidManager. */
#include <IOKit/hid/IOHIDManager.h>
#include <IOKit/hid/IOHIDKeys.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/usb/USBSpec.h>
#include <CoreFoundation/CoreFoundation.h>
+#include <mach/mach_error.h>
+#include <stdbool.h>
#include <wchar.h>
+#include <locale.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>
+#include <dlfcn.h>
-#include "../hidapi/hidapi.h"
+#include "hidapi_darwin.h"
-#define VALVE_USB_VID 0x28DE
+/* As defined in AppKit.h, but we don't need the entire AppKit for a single constant. */
+extern const double NSAppKitVersionNumber;
/* Barrier implementation because Mac OSX doesn't have pthread_barrier.
- It also doesn't have clock_gettime(). So much for POSIX and SUSv2.
- This implementation came from Brent Priddy and was posted on
- StackOverflow. It is used with his permission. */
+ It also doesn't have clock_gettime(). So much for POSIX and SUSv2.
+ This implementation came from Brent Priddy and was posted on
+ StackOverflow. It is used with his permission. */
typedef int pthread_barrierattr_t;
typedef struct pthread_barrier {
- pthread_mutex_t mutex;
- pthread_cond_t cond;
- int count;
- int trip_count;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ int count;
+ int trip_count;
} pthread_barrier_t;
static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
{
+ (void) attr;
+
if(count == 0) {
errno = EINVAL;
return -1;
}
-
+
if(pthread_mutex_init(&barrier->mutex, 0) < 0) {
return -1;
}
@@ -64,7 +71,7 @@
}
barrier->trip_count = count;
barrier->count = 0;
-
+
return 0;
}
@@ -103,10 +110,23 @@
struct input_report *next;
};
+static struct hid_api_version api_version = {
+ .major = HID_API_VERSION_MAJOR,
+ .minor = HID_API_VERSION_MINOR,
+ .patch = HID_API_VERSION_PATCH
+};
+
+/* - Run context - */
+static IOHIDManagerRef hid_mgr = 0x0;
+static int is_macos_10_10_or_greater = 0;
+static IOOptionBits device_open_options = 0;
+static wchar_t *last_global_error_str = NULL;
+/* --- */
+
struct hid_device_ {
IOHIDDeviceRef device_handle;
+ IOOptionBits open_options;
int blocking;
- int uses_numbered_reports;
int disconnected;
CFStringRef run_loop_mode;
CFRunLoopRef run_loop;
@@ -114,113 +134,177 @@
uint8_t *input_report_buf;
CFIndex max_input_report_len;
struct input_report *input_reports;
-
+ struct hid_device_info* device_info;
+
pthread_t thread;
pthread_mutex_t mutex; /* Protects input_reports */
pthread_cond_t condition;
pthread_barrier_t barrier; /* Ensures correct startup sequence */
pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */
int shutdown_thread;
+ wchar_t *last_error_str;
};
-struct hid_device_list_node
-{
- struct hid_device_ *dev;
- struct hid_device_list_node *next;
-};
-
-static IOHIDManagerRef hid_mgr = 0x0;
-static struct hid_device_list_node *device_list = 0x0;
-
static hid_device *new_hid_device(void)
{
- hid_device *dev = (hid_device*)calloc(1, sizeof(hid_device));
+ hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
+ if (dev == NULL) {
+ return NULL;
+ }
+
dev->device_handle = NULL;
+ dev->open_options = device_open_options;
dev->blocking = 1;
- dev->uses_numbered_reports = 0;
dev->disconnected = 0;
dev->run_loop_mode = NULL;
dev->run_loop = NULL;
dev->source = NULL;
dev->input_report_buf = NULL;
dev->input_reports = NULL;
+ dev->device_info = NULL;
dev->shutdown_thread = 0;
-
+ dev->last_error_str = NULL;
+
/* Thread objects */
pthread_mutex_init(&dev->mutex, NULL);
pthread_cond_init(&dev->condition, NULL);
pthread_barrier_init(&dev->barrier, NULL, 2);
pthread_barrier_init(&dev->shutdown_barrier, NULL, 2);
-
+
return dev;
}
static void free_hid_device(hid_device *dev)
{
- struct input_report *rpt;
if (!dev)
return;
-
+
/* Delete any input reports still left over. */
- rpt = dev->input_reports;
+ struct input_report *rpt = dev->input_reports;
while (rpt) {
struct input_report *next = rpt->next;
free(rpt->data);
free(rpt);
rpt = next;
}
-
+
/* Free the string and the report buffer. The check for NULL
- is necessary here as CFRelease() doesn't handle NULL like
- free() and others do. */
+ is necessary here as CFRelease() doesn't handle NULL like
+ free() and others do. */
if (dev->run_loop_mode)
CFRelease(dev->run_loop_mode);
if (dev->source)
CFRelease(dev->source);
free(dev->input_report_buf);
+ hid_free_enumeration(dev->device_info);
- if (device_list) {
- if (device_list->dev == dev) {
- device_list = device_list->next;
- }
- else {
- struct hid_device_list_node *node = device_list;
- while (node) {
- if (node->next && node->next->dev == dev) {
- struct hid_device_list_node *new_next = node->next->next;
- free(node->next);
- node->next = new_next;
- break;
- }
-
- node = node->next;
- }
- }
- }
-
/* Clean up the thread objects */
pthread_barrier_destroy(&dev->shutdown_barrier);
pthread_barrier_destroy(&dev->barrier);
pthread_cond_destroy(&dev->condition);
pthread_mutex_destroy(&dev->mutex);
-
+
/* Free the structure itself. */
free(dev);
}
-#if 0
-static void register_error(hid_device *device, const char *op)
-{
-
-}
-#endif
+/* The caller must free the returned string with free(). */
+static wchar_t *utf8_to_wchar_t(const char *utf8)
+{
+ wchar_t *ret = NULL;
+
+ if (utf8) {
+ size_t wlen = mbstowcs(NULL, utf8, 0);
+ if ((size_t) -1 == wlen) {
+ return wcsdup(L"");
+ }
+ ret = (wchar_t*) calloc(wlen+1, sizeof(wchar_t));
+ if (ret == NULL) {
+ /* as much as we can do at this point */
+ return NULL;
+ }
+ mbstowcs(ret, utf8, wlen+1);
+ ret[wlen] = 0x0000;
+ }
+
+ return ret;
+}
+
+
+/* Makes a copy of the given error message (and decoded according to the
+ * currently locale) into the wide string pointer pointed by error_str.
+ * The last stored error string is freed.
+ * Use register_error_str(NULL) to free the error message completely. */
+static void register_error_str(wchar_t **error_str, const char *msg)
+{
+ free(*error_str);
+ *error_str = utf8_to_wchar_t(msg);
+}
+
+/* Similar to register_error_str, but allows passing a format string with va_list args into this function. */
+static void register_error_str_vformat(wchar_t **error_str, const char *format, va_list args)
+{
+ char msg[1024];
+ vsnprintf(msg, sizeof(msg), format, args);
+
+ register_error_str(error_str, msg);
+}
+
+/* Set the last global error to be reported by hid_error(NULL).
+ * The given error message will be copied (and decoded according to the
+ * currently locale, so do not pass in string constants).
+ * The last stored global error message is freed.
+ * Use register_global_error(NULL) to indicate "no error". */
+static void register_global_error(const char *msg)
+{
+ register_error_str(&last_global_error_str, msg);
+}
+
+/* Similar to register_global_error, but allows passing a format string into this function. */
+static void register_global_error_format(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ register_error_str_vformat(&last_global_error_str, format, args);
+ va_end(args);
+}
+
+/* Set the last error for a device to be reported by hid_error(dev).
+ * The given error message will be copied (and decoded according to the
+ * currently locale, so do not pass in string constants).
+ * The last stored device error message is freed.
+ * Use register_device_error(dev, NULL) to indicate "no error". */
+static void register_device_error(hid_device *dev, const char *msg)
+{
+ register_error_str(&dev->last_error_str, msg);
+}
+
+/* Similar to register_device_error, but you can pass a format string into this function. */
+static void register_device_error_format(hid_device *dev, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ register_error_str_vformat(&dev->last_error_str, format, args);
+ va_end(args);
+}
+
+
+static CFArrayRef get_array_property(IOHIDDeviceRef device, CFStringRef key)
+{
+ CFTypeRef ref = IOHIDDeviceGetProperty(device, key);
+ if (ref != NULL && CFGetTypeID(ref) == CFArrayGetTypeID()) {
+ return (CFArrayRef)ref;
+ } else {
+ return NULL;
+ }
+}
static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key)
{
CFTypeRef ref;
- int32_t value;
-
+ int32_t value = 0;
+
ref = IOHIDDeviceGetProperty(device, key);
if (ref) {
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
@@ -231,6 +315,41 @@
return 0;
}
+static bool try_get_int_property(IOHIDDeviceRef device, CFStringRef key, int32_t *out_val)
+{
+ bool result = false;
+ CFTypeRef ref;
+
+ ref = IOHIDDeviceGetProperty(device, key);
+ if (ref) {
+ if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
+ result = CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, out_val);
+ }
+ }
+ return result;
+}
+
+static bool try_get_ioregistry_int_property(io_service_t service, CFStringRef property, int32_t *out_val)
+{
+ bool result = false;
+ CFTypeRef ref = IORegistryEntryCreateCFProperty(service, property, kCFAllocatorDefault, 0);
+
+ if (ref) {
+ if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
+ result = CFNumberGetValue(ref, kCFNumberSInt32Type, out_val);
+ }
+
+ CFRelease(ref);
+ }
+
+ return result;
+}
+
+static CFArrayRef get_usage_pairs(IOHIDDeviceRef device)
+{
+ return get_array_property(device, CFSTR(kIOHIDDeviceUsagePairsKey));
+}
+
static unsigned short get_vendor_id(IOHIDDeviceRef device)
{
return get_int_property(device, CFSTR(kIOHIDVendorIDKey));
@@ -241,7 +360,6 @@
return get_int_property(device, CFSTR(kIOHIDProductIDKey));
}
-
static int32_t get_max_report_length(IOHIDDeviceRef device)
{
return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey));
@@ -250,78 +368,45 @@
static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len)
{
CFStringRef str;
-
+
if (!len)
return 0;
- if (CFGetTypeID(prop) != CFStringGetTypeID())
- return 0;
+ str = (CFStringRef) IOHIDDeviceGetProperty(device, prop);
- str = (CFStringRef)IOHIDDeviceGetProperty(device, prop);
-
buf[0] = 0;
-
- if (str && CFGetTypeID(str) == CFStringGetTypeID()) {
- CFIndex used_buf_len, chars_copied;
- CFRange range;
+
+ if (str) {
CFIndex str_len = CFStringGetLength(str);
+ CFRange range;
+ CFIndex used_buf_len;
+ CFIndex chars_copied;
+
len --;
+
range.location = 0;
- range.length = (str_len > len)? len: str_len;
+ range.length = ((size_t) str_len > len)? len: (size_t) str_len;
chars_copied = CFStringGetBytes(str,
- range,
- kCFStringEncodingUTF32LE,
- (char)'?',
- FALSE,
- (UInt8*)buf,
- len,
- &used_buf_len);
-
- buf[chars_copied] = 0;
- return (int)chars_copied;
+ range,
+ kCFStringEncodingUTF32LE,
+ (char) '?',
+ FALSE,
+ (UInt8*)buf,
+ len * sizeof(wchar_t),
+ &used_buf_len);
+
+ if (chars_copied <= 0)
+ buf[0] = 0;
+ else
+ buf[chars_copied] = 0;
+
+ return 0;
}
else
- return 0;
-
+ return -1;
+
}
-static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, char *buf, size_t len)
-{
- CFStringRef str;
- if (!len)
- return 0;
-
- if (CFGetTypeID(prop) != CFStringGetTypeID())
- return 0;
-
- str = (CFStringRef)IOHIDDeviceGetProperty(device, prop);
-
- buf[0] = 0;
-
- if (str && CFGetTypeID(str) == CFStringGetTypeID()) {
- CFIndex used_buf_len, chars_copied;
- CFRange range;
- CFIndex str_len = CFStringGetLength(str);
- len--;
- range.location = 0;
- range.length = (str_len > len)? len: str_len;
- chars_copied = CFStringGetBytes(str,
- range,
- kCFStringEncodingUTF8,
- (char)'?',
- FALSE,
- (UInt8*)buf,
- len,
- &used_buf_len);
-
- buf[chars_copied] = 0;
- return (int)used_buf_len;
- }
- else
- return 0;
-}
-
-
static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len)
{
return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len);
@@ -342,151 +427,50 @@
static wchar_t *dup_wcs(const wchar_t *s)
{
size_t len = wcslen(s);
- wchar_t *ret = (wchar_t *)malloc((len+1)*sizeof(wchar_t));
+ wchar_t *ret = (wchar_t*) malloc((len+1)*sizeof(wchar_t));
wcscpy(ret, s);
-
+
return ret;
}
-
-static int make_path(IOHIDDeviceRef device, char *buf, size_t len)
-{
- int res;
- unsigned short vid, pid;
- char transport[32];
-
- buf[0] = '\0';
-
- res = get_string_property_utf8(
- device, CFSTR(kIOHIDTransportKey),
- transport, sizeof(transport));
-
- if (!res)
- return -1;
-
- vid = get_vendor_id(device);
- pid = get_product_id(device);
-
- res = snprintf(buf, len, "%s_%04hx_%04hx_%p",
- transport, vid, pid, device);
-
-
- buf[len-1] = '\0';
- return res+1;
-}
-
-static void hid_device_removal_callback(void *context, IOReturn result,
- void *sender, IOHIDDeviceRef hid_ref)
-{
- // The device removal callback is sometimes called even after being
- // unregistered, leading to a crash when trying to access fields in
- // the already freed hid_device. We keep a linked list of all created
- // hid_device's so that the one being removed can be checked against
- // the list to see if it really hasn't been closed yet and needs to
- // be dealt with here.
- struct hid_device_list_node *node = device_list;
- while (node) {
- if (node->dev->device_handle == hid_ref) {
- node->dev->disconnected = 1;
- CFRunLoopStop(node->dev->run_loop);
- break;
- }
-
- node = node->next;
- }
-}
-
-static CFDictionaryRef
-create_usage_match(const UInt32 page, const UInt32 usage, int *okay)
-{
- CFDictionaryRef retval = NULL;
- CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
- CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
- const void *keys[2] = { (void *) CFSTR(kIOHIDDeviceUsagePageKey), (void *) CFSTR(kIOHIDDeviceUsageKey) };
- const void *vals[2] = { (void *) pageNumRef, (void *) usageNumRef };
-
- if (pageNumRef && usageNumRef) {
- retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- }
-
- if (pageNumRef) {
- CFRelease(pageNumRef);
- }
- if (usageNumRef) {
- CFRelease(usageNumRef);
- }
-
- if (!retval) {
- *okay = 0;
- }
-
- return retval;
-}
-
-static CFDictionaryRef
-create_vendor_match(const UInt32 vendor, int *okay)
-{
- CFDictionaryRef retval = NULL;
- CFNumberRef vidNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendor);
- const void *keys[1] = { (void *) CFSTR(kIOHIDVendorIDKey) };
- const void *vals[1] = { (void *) vidNumRef };
-
- if (vidNumRef) {
- retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- CFRelease(vidNumRef);
- }
-
- if (!retval) {
- *okay = 0;
- }
-
- return retval;
-}
-
/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */
static int init_hid_manager(void)
{
- int okay = 1;
- const void *vals[] = {
- (void *) create_usage_match(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
- (void *) create_usage_match(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
- (void *) create_usage_match(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
- (void *) create_vendor_match(VALVE_USB_VID, &okay),
- };
- const size_t numElements = SDL_arraysize(vals);
- CFArrayRef matchingArray = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) : NULL;
- size_t i;
-
- for (i = 0; i < numElements; i++) {
- if (vals[i]) {
- CFRelease((CFTypeRef) vals[i]);
- }
- }
-
/* Initialize all the HID Manager Objects */
hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
if (hid_mgr) {
- IOHIDManagerSetDeviceMatchingMultiple(hid_mgr, matchingArray);
+ IOHIDManagerSetDeviceMatching(hid_mgr, NULL);
IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
- IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, hid_device_removal_callback, NULL);
- }
-
- if (matchingArray != NULL) {
- CFRelease(matchingArray);
+ return 0;
}
- return hid_mgr ? 0 : -1;
+ register_global_error("Failed to create IOHIDManager");
+ return -1;
+}
+
+HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void)
+{
+ return &api_version;
+}
+
+HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
+{
+ return HID_API_VERSION_STR;
}
/* Initialize the IOHIDManager if necessary. This is the public function, and
- it is safe to call this function repeatedly. Return 0 for success and -1
- for failure. */
+ it is safe to call this function repeatedly. Return 0 for success and -1
+ for failure. */
int HID_API_EXPORT hid_init(void)
{
+ register_global_error(NULL);
+
if (!hid_mgr) {
+ is_macos_10_10_or_greater = (NSAppKitVersionNumber >= 1343); /* NSAppKitVersionNumber10_10 */
+ hid_darwin_set_open_exclusive(1); /* Backward compatibility */
return init_hid_manager();
}
-
+
/* Already initialized. */
return 0;
}
@@ -499,7 +483,10 @@
CFRelease(hid_mgr);
hid_mgr = NULL;
}
-
+
+ /* Free global error message */
+ register_global_error(NULL);
+
return 0;
}
@@ -510,124 +497,296 @@
} while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut);
}
-struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
+static int read_usb_interface_from_hid_service_parent(io_service_t hid_service)
{
- struct hid_device_info *root = NULL; // return object
- struct hid_device_info *cur_dev = NULL;
- CFIndex num_devices;
- CFSetRef device_set;
- IOHIDDeviceRef *device_array;
- int i;
- const char *hint = SDL_GetHint(SDL_HINT_HIDAPI_IGNORE_DEVICES);
-
- /* Set up the HID Manager if it hasn't been done */
- if (hid_init() < 0)
- return NULL;
-
- /* give the IOHIDManager a chance to update itself */
- process_pending_events();
-
- /* Get a list of the Devices */
- device_set = IOHIDManagerCopyDevices(hid_mgr);
- if (!device_set)
- return NULL;
+ int32_t result = -1;
+ bool success = false;
+ io_registry_entry_t current = IO_OBJECT_NULL;
+ kern_return_t res;
+ int parent_number = 0;
- /* Convert the list into a C array so we can iterate easily. */
- num_devices = CFSetGetCount(device_set);
- if (!num_devices) {
- CFRelease(device_set);
+ res = IORegistryEntryGetParentEntry(hid_service, kIOServicePlane, ¤t);
+ while (KERN_SUCCESS == res
+ /* Only search up to 3 parent entries.
+ * With the default driver - the parent-of-interest supposed to be the first one,
+ * but lets assume some custom drivers or so, with deeper tree. */
+ && parent_number < 3) {
+ io_registry_entry_t parent = IO_OBJECT_NULL;
+ int32_t interface_number = -1;
+ parent_number++;
+
+ success = try_get_ioregistry_int_property(current, CFSTR(kUSBInterfaceNumber), &interface_number);
+ if (success) {
+ result = interface_number;
+ break;
+ }
+
+ res = IORegistryEntryGetParentEntry(current, kIOServicePlane, &parent);
+ if (parent) {
+ IOObjectRelease(current);
+ current = parent;
+ }
+
+ }
+
+ if (current) {
+ IOObjectRelease(current);
+ current = IO_OBJECT_NULL;
+ }
+
+ return result;
+}
+
+static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, int32_t usage_page, int32_t usage)
+{
+ unsigned short dev_vid;
+ unsigned short dev_pid;
+ int BUF_LEN = 256;
+ wchar_t buf[BUF_LEN];
+ CFTypeRef transport_prop;
+
+ struct hid_device_info *cur_dev;
+ io_service_t hid_service;
+ kern_return_t res;
+ uint64_t entry_id = 0;
+
+ if (dev == NULL) {
return NULL;
}
- device_array = (IOHIDDeviceRef*)calloc(num_devices, sizeof(IOHIDDeviceRef));
- CFSetGetValues(device_set, (const void **) device_array);
-
- /* Iterate over each device, making an entry for it. */
+
+ cur_dev = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info));
+ if (cur_dev == NULL) {
+ return NULL;
+ }
+
+ dev_vid = get_vendor_id(dev);
+ dev_pid = get_product_id(dev);
+
+ cur_dev->usage_page = usage_page;
+ cur_dev->usage = usage;
+
+ /* Fill out the record */
+ cur_dev->next = NULL;
+
+ /* Fill in the path (as a unique ID of the service entry) */
+ cur_dev->path = NULL;
+ hid_service = IOHIDDeviceGetService(dev);
+ if (hid_service != MACH_PORT_NULL) {
+ res = IORegistryEntryGetRegistryEntryID(hid_service, &entry_id);
+ }
+ else {
+ res = KERN_INVALID_ARGUMENT;
+ }
+
+ if (res == KERN_SUCCESS) {
+ /* max value of entry_id(uint64_t) is 18446744073709551615 which is 20 characters long,
+ so for (max) "path" string 'DevSrvsID:18446744073709551615' we would need
+ 9+1+20+1=31 bytes buffer, but allocate 32 for simple alignment */
+ const size_t path_len = 32;
+ cur_dev->path = calloc(1, path_len);
+ if (cur_dev->path != NULL) {
+ snprintf(cur_dev->path, path_len, "DevSrvsID:%llu", entry_id);
+ }
+ }
+
+ if (cur_dev->path == NULL) {
+ /* for whatever reason, trying to keep it a non-NULL string */
+ cur_dev->path = strdup("");
+ }
+
+ /* Serial Number */
+ get_serial_number(dev, buf, BUF_LEN);
+ cur_dev->serial_number = dup_wcs(buf);
+
+ /* Manufacturer and Product strings */
+ get_manufacturer_string(dev, buf, BUF_LEN);
+ cur_dev->manufacturer_string = dup_wcs(buf);
+ get_product_string(dev, buf, BUF_LEN);
+ cur_dev->product_string = dup_wcs(buf);
+
+ /* VID/PID */
+ cur_dev->vendor_id = dev_vid;
+ cur_dev->product_id = dev_pid;
+
+ /* Release Number */
+ cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey));
+
+ /* Interface Number.
+ * We can only retrieve the interface number for USB HID devices.
+ * See below */
+ cur_dev->interface_number = -1;
+
+ /* Bus Type */
+ transport_prop = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDTransportKey));
+
+ if (transport_prop != NULL && CFGetTypeID(transport_prop) == CFStringGetTypeID()) {
+ if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportUSBValue), 0) == kCFCompareEqualTo) {
+ int32_t interface_number = -1;
+ cur_dev->bus_type = HID_API_BUS_USB;
+
+ /* A IOHIDDeviceRef used to have this simple property,
+ * until macOS 13.3 - we will try to use it. */
+ if (try_get_int_property(dev, CFSTR(kUSBInterfaceNumber), &interface_number)) {
+ cur_dev->interface_number = interface_number;
+ } else {
+ /* Otherwise fallback to io_service_t property.
+ * (of one of the parent services). */
+ cur_dev->interface_number = read_usb_interface_from_hid_service_parent(hid_service);
+
+ /* If the above doesn't work -
+ * no (known) fallback exists at this point. */
+ }
+
+ /* Match "Bluetooth", "BluetoothLowEnergy" and "Bluetooth Low Energy" strings */
+ } else if (CFStringHasPrefix((CFStringRef)transport_prop, CFSTR(kIOHIDTransportBluetoothValue))) {
+ cur_dev->bus_type = HID_API_BUS_BLUETOOTH;
+ } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportI2CValue), 0) == kCFCompareEqualTo) {
+ cur_dev->bus_type = HID_API_BUS_I2C;
+ } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportSPIValue), 0) == kCFCompareEqualTo) {
+ cur_dev->bus_type = HID_API_BUS_SPI;
+ }
+ }
+
+ return cur_dev;
+}
+
+static struct hid_device_info *create_device_info(IOHIDDeviceRef device)
+{
+ const int32_t primary_usage_page = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey));
+ const int32_t primary_usage = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey));
+
+ /* Primary should always be first, to match previous behavior. */
+ struct hid_device_info *root = create_device_info_with_usage(device, primary_usage_page, primary_usage);
+ struct hid_device_info *cur = root;
+
+ if (!root)
+ return NULL;
+
+ CFArrayRef usage_pairs = get_usage_pairs(device);
+
+ if (usage_pairs != NULL) {
+ struct hid_device_info *next = NULL;
+ for (CFIndex i = 0; i < CFArrayGetCount(usage_pairs); i++) {
+ CFTypeRef dict = CFArrayGetValueAtIndex(usage_pairs, i);
+ if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) {
+ continue;
+ }
+
+ CFTypeRef usage_page_ref, usage_ref;
+ int32_t usage_page, usage;
+
+ if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsagePageKey), &usage_page_ref) ||
+ !CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsageKey), &usage_ref) ||
+ CFGetTypeID(usage_page_ref) != CFNumberGetTypeID() ||
+ CFGetTypeID(usage_ref) != CFNumberGetTypeID() ||
+ !CFNumberGetValue((CFNumberRef)usage_page_ref, kCFNumberSInt32Type, &usage_page) ||
+ !CFNumberGetValue((CFNumberRef)usage_ref, kCFNumberSInt32Type, &usage)) {
+ continue;
+ }
+ if (usage_page == primary_usage_page && usage == primary_usage)
+ continue; /* Already added. */
+
+ next = create_device_info_with_usage(device, usage_page, usage);
+ cur->next = next;
+ if (next != NULL) {
+ cur = next;
+ }
+ }
+ }
+
+ return root;
+}
+
+struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
+{
+ struct hid_device_info *root = NULL; /* return object */
+ struct hid_device_info *cur_dev = NULL;
+ CFIndex num_devices;
+ int i;
+
+ /* Set up the HID Manager if it hasn't been done */
+ if (hid_init() < 0) {
+ return NULL;
+ }
+ /* register_global_error: global error is set/reset by hid_init */
+
+ /* give the IOHIDManager a chance to update itself */
+ process_pending_events();
+
+ /* Get a list of the Devices */
+ CFMutableDictionaryRef matching = NULL;
+ if (vendor_id != 0 || product_id != 0) {
+ matching = CFDictionaryCreateMutable(kCFAllocatorDefault, kIOHIDOptionsTypeNone, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ if (matching && vendor_id != 0) {
+ CFNumberRef v = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &vendor_id);
+ CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), v);
+ CFRelease(v);
+ }
+
+ if (matching && product_id != 0) {
+ CFNumberRef p = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &product_id);
+ CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), p);
+ CFRelease(p);
+ }
+ }
+ IOHIDManagerSetDeviceMatching(hid_mgr, matching);
+ if (matching != NULL) {
+ CFRelease(matching);
+ }
+
+ CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
+
+ IOHIDDeviceRef *device_array = NULL;
+
+ if (device_set != NULL) {
+ /* Convert the list into a C array so we can iterate easily. */
+ num_devices = CFSetGetCount(device_set);
+ device_array = (IOHIDDeviceRef*) calloc(num_devices, sizeof(IOHIDDeviceRef));
+ CFSetGetValues(device_set, (const void **) device_array);
+ } else {
+ num_devices = 0;
+ }
+
+ /* Iterate over each device, making an entry for it. */
for (i = 0; i < num_devices; i++) {
- unsigned short dev_vid;
- unsigned short dev_pid;
-#define BUF_LEN 256
- wchar_t buf[BUF_LEN];
- char cbuf[BUF_LEN];
-
+
IOHIDDeviceRef dev = device_array[i];
-
if (!dev) {
continue;
}
-#if 0 // Prefer direct HID support as that has extended functionality
-#ifdef SDL_JOYSTICK_MFI
- // We want to prefer Game Controller support where available,
- // as Apple will likely be requiring that for supported devices.
- extern SDL_bool IOS_SupportedHIDDevice(IOHIDDeviceRef device);
- if (IOS_SupportedHIDDevice(dev)) {
+ struct hid_device_info *tmp = create_device_info(dev);
+ if (tmp == NULL) {
continue;
}
-#endif
-#endif
- dev_vid = get_vendor_id(dev);
- dev_pid = get_product_id(dev);
-
- /* See if there are any devices we should skip in enumeration */
- if (hint) {
- char vendor_match[16], product_match[16];
- SDL_snprintf(vendor_match, sizeof(vendor_match), "0x%.4x/0x0000", dev_vid);
- SDL_snprintf(product_match, sizeof(product_match), "0x%.4x/0x%.4x", dev_vid, dev_pid);
- if (SDL_strcasestr(hint, vendor_match) || SDL_strcasestr(hint, product_match)) {
- continue;
- }
+ if (cur_dev) {
+ cur_dev->next = tmp;
}
+ else {
+ root = tmp;
+ }
+ cur_dev = tmp;
- /* Check the VID/PID against the arguments */
- if ((vendor_id == 0x0 || dev_vid == vendor_id) &&
- (product_id == 0x0 || dev_pid == product_id)) {
- struct hid_device_info *tmp;
-
- /* VID/PID match. Create the record. */
- tmp = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info));
- if (cur_dev) {
- cur_dev->next = tmp;
- }
- else {
- root = tmp;
- }
- cur_dev = tmp;
-
- // Get the Usage Page and Usage for this device.
- cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey));
- cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey));
-
- /* Fill out the record */
- cur_dev->next = NULL;
- make_path(dev, cbuf, sizeof(cbuf));
- cur_dev->path = strdup(cbuf);
-
- /* Serial Number */
- get_serial_number(dev, buf, BUF_LEN);
- cur_dev->serial_number = dup_wcs(buf);
-
- /* Manufacturer and Product strings */
- get_manufacturer_string(dev, buf, BUF_LEN);
- cur_dev->manufacturer_string = dup_wcs(buf);
- get_product_string(dev, buf, BUF_LEN);
- cur_dev->product_string = dup_wcs(buf);
-
- /* VID/PID */
- cur_dev->vendor_id = dev_vid;
- cur_dev->product_id = dev_pid;
-
- /* Release Number */
- cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey));
-
- /* Interface Number (Unsupported on Mac)*/
- cur_dev->interface_number = -1;
+ /* move the pointer to the tail of returnd list */
+ while (cur_dev->next != NULL) {
+ cur_dev = cur_dev->next;
}
}
-
+
free(device_array);
- CFRelease(device_set);
-
+ if (device_set != NULL)
+ CFRelease(device_set);
+
+ if (root == NULL) {
+ if (vendor_id == 0 && product_id == 0) {
+ register_global_error("No HID devices found in the system.");
+ } else {
+ register_global_error("No HID devices with requested VID/PID found in the system.");
+ }
+ }
+
return root;
}
@@ -649,11 +808,18 @@
hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
{
/* This function is identical to the Linux version. Platform independent. */
+
struct hid_device_info *devs, *cur_dev;
const char *path_to_open = NULL;
hid_device * handle = NULL;
-
+
+ /* register_global_error: global error is reset by hid_enumerate/hid_init */
devs = hid_enumerate(vendor_id, product_id);
+ if (devs == NULL) {
+ /* register_global_error: global error is already set by hid_enumerate */
+ return NULL;
+ }
+
cur_dev = devs;
while (cur_dev) {
if (cur_dev->vendor_id == vendor_id &&
@@ -671,37 +837,56 @@
}
cur_dev = cur_dev->next;
}
-
+
if (path_to_open) {
- /* Open the device */
- handle = hid_open_path(path_to_open, 0);
+ handle = hid_open_path(path_to_open);
+ } else {
+ register_global_error("Device with requested VID/PID/(SerialNumber) not found");
}
-
+
hid_free_enumeration(devs);
-
+
return handle;
}
-/* The Run Loop calls this function for each input report received.
- This function puts the data into a linked list to be picked up by
- hid_read(). */
-static void hid_report_callback(void *context, IOReturn result, void *sender,
- IOHIDReportType report_type, uint32_t report_id,
- uint8_t *report, CFIndex report_length)
+static void hid_device_removal_callback(void *context, IOReturn result,
+ void *sender)
{
+ (void) result;
+ (void) sender;
+
+ /* Stop the Run Loop for this device. */
+ hid_device *d = (hid_device*) context;
+
+ d->disconnected = 1;
+ CFRunLoopStop(d->run_loop);
+}
+
+/* The Run Loop calls this function for each input report received.
+ This function puts the data into a linked list to be picked up by
+ hid_read(). */
+static void hid_report_callback(void *context, IOReturn result, void *sender,
+ IOHIDReportType report_type, uint32_t report_id,
+ uint8_t *report, CFIndex report_length)
+{
+ (void) result;
+ (void) sender;
+ (void) report_type;
+ (void) report_id;
+
struct input_report *rpt;
- hid_device *dev = (hid_device *)context;
-
+ hid_device *dev = (hid_device*) context;
+
/* Make a new Input Report object */
- rpt = (struct input_report *)calloc(1, sizeof(struct input_report));
- rpt->data = (uint8_t *)calloc(1, report_length);
+ rpt = (struct input_report*) calloc(1, sizeof(struct input_report));
+ rpt->data = (uint8_t*) calloc(1, report_length);
memcpy(rpt->data, report, report_length);
rpt->len = report_length;
rpt->next = NULL;
-
+
/* Lock this section */
pthread_mutex_lock(&dev->mutex);
-
+
/* Attach the new report object to the end of the list. */
if (dev->input_reports == NULL) {
/* The list is empty. Put it at the root. */
@@ -716,58 +901,58 @@
num_queued++;
}
cur->next = rpt;
-
+
/* Pop one off if we've reached 30 in the queue. This
- way we don't grow forever if the user never reads
- anything from the device. */
+ way we don't grow forever if the user never reads
+ anything from the device. */
if (num_queued > 30) {
return_data(dev, NULL, 0);
}
}
-
+
/* Signal a waiting thread that there is data. */
pthread_cond_signal(&dev->condition);
-
+
/* Unlock */
pthread_mutex_unlock(&dev->mutex);
-
+
}
-/* This gets called when the read_thred's run loop gets signaled by
- hid_close(), and serves to stop the read_thread's run loop. */
+/* This gets called when the read_thread's run loop gets signaled by
+ hid_close(), and serves to stop the read_thread's run loop. */
static void perform_signal_callback(void *context)
{
- hid_device *dev = (hid_device *)context;
- CFRunLoopStop(dev->run_loop); //TODO: CFRunLoopGetCurrent()
+ hid_device *dev = (hid_device*) context;
+ CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/
}
static void *read_thread(void *param)
{
- hid_device *dev = (hid_device *)param;
- CFRunLoopSourceContext ctx;
- SInt32 code;
-
+ hid_device *dev = (hid_device*) param;
+ SInt32 code;
+
/* Move the device's run loop to this thread. */
IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode);
-
+
/* Create the RunLoopSource which is used to signal the
- event loop to stop when hid_close() is called. */
+ event loop to stop when hid_close() is called. */
+ CFRunLoopSourceContext ctx;
memset(&ctx, 0, sizeof(ctx));
ctx.version = 0;
ctx.info = dev;
ctx.perform = &perform_signal_callback;
dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx);
CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode);
-
+
/* Store off the Run Loop so it can be stopped from hid_close()
- and on device disconnection. */
+ and on device disconnection. */
dev->run_loop = CFRunLoopGetCurrent();
-
+
/* Notify the main thread that the read thread is up and running. */
pthread_barrier_wait(&dev->barrier);
-
+
/* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input
- reports into the hid_report_callback(). */
+ reports into the hid_report_callback(). */
while (!dev->shutdown_thread && !dev->disconnected) {
code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE);
/* Return if the device has been disconnected */
@@ -775,171 +960,220 @@
dev->disconnected = 1;
break;
}
-
-
+
+
/* Break if The Run Loop returns Finished or Stopped. */
if (code != kCFRunLoopRunTimedOut &&
code != kCFRunLoopRunHandledSource) {
/* There was some kind of error. Setting
- shutdown seems to make sense, but
- there may be something else more appropriate */
+ shutdown seems to make sense, but
+ there may be something else more appropriate */
dev->shutdown_thread = 1;
break;
}
}
-
+
/* Now that the read thread is stopping, Wake any threads which are
- waiting on data (in hid_read_timeout()). Do this under a mutex to
- make sure that a thread which is about to go to sleep waiting on
- the condition acutally will go to sleep before the condition is
- signaled. */
+ waiting on data (in hid_read_timeout()). Do this under a mutex to
+ make sure that a thread which is about to go to sleep waiting on
+ the condition actually will go to sleep before the condition is
+ signaled. */
pthread_mutex_lock(&dev->mutex);
pthread_cond_broadcast(&dev->condition);
pthread_mutex_unlock(&dev->mutex);
-
+
/* Wait here until hid_close() is called and makes it past
- the call to CFRunLoopWakeUp(). This thread still needs to
- be valid when that function is called on the other thread. */
+ the call to CFRunLoopWakeUp(). This thread still needs to
+ be valid when that function is called on the other thread. */
pthread_barrier_wait(&dev->shutdown_barrier);
-
+
return NULL;
}
-hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive)
+/* \p path must be one of:
+ - in format 'DevSrvsID:<RegistryEntryID>' (as returned by hid_enumerate);
+ - a valid path to an IOHIDDevice in the IOService plane (as returned by IORegistryEntryGetPath,
+ e.g.: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver");
+ Second format is for compatibility with paths accepted by older versions of HIDAPI.
+*/
+static io_registry_entry_t hid_open_service_registry_from_path(const char *path)
{
- int i;
- hid_device *dev = NULL;
- CFIndex num_devices;
- CFSetRef device_set;
- IOHIDDeviceRef *device_array;
+ if (path == NULL)
+ return MACH_PORT_NULL;
- dev = new_hid_device();
-
- /* Set up the HID Manager if it hasn't been done */
- if (hid_init() < 0)
- return NULL;
-
-#if 0 /* We have a path because the IOHIDManager is already updated */
- /* give the IOHIDManager a chance to update itself */
- process_pending_events();
-#endif
-
- device_set = IOHIDManagerCopyDevices(hid_mgr);
- if (!device_set)
- return NULL;
-
- num_devices = CFSetGetCount(device_set);
- device_array = (IOHIDDeviceRef *)calloc(num_devices, sizeof(IOHIDDeviceRef));
- CFSetGetValues(device_set, (const void **) device_array);
- for (i = 0; i < num_devices; i++) {
- char cbuf[BUF_LEN];
- IOHIDDeviceRef os_dev = device_array[i];
-
- make_path(os_dev, cbuf, sizeof(cbuf));
- if (!strcmp(cbuf, path)) {
- // Matched Paths. Open this Device.
- IOReturn ret = IOHIDDeviceOpen(os_dev, kIOHIDOptionsTypeNone);
- if (ret == kIOReturnSuccess) {
- char str[32];
- struct hid_device_list_node *node;
-
- free(device_array);
- CFRelease(device_set);
- dev->device_handle = os_dev;
-
- /* Create the buffers for receiving data */
- dev->max_input_report_len = (CFIndex) get_max_report_length(os_dev);
- SDL_assert(dev->max_input_report_len > 0);
- dev->input_report_buf = (uint8_t *)calloc(dev->max_input_report_len, sizeof(uint8_t));
-
- /* Create the Run Loop Mode for this device.
- printing the reference seems to work. */
- snprintf(str, sizeof(str), "HIDAPI_%p", os_dev);
- dev->run_loop_mode =
- CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
-
- /* Attach the device to a Run Loop */
- IOHIDDeviceRegisterInputReportCallback(
- os_dev, dev->input_report_buf, dev->max_input_report_len,
- &hid_report_callback, dev);
-
- node = (struct hid_device_list_node *)calloc(1, sizeof(struct hid_device_list_node));
- node->dev = dev;
- node->next = device_list;
- device_list = node;
-
- /* Start the read thread */
- pthread_create(&dev->thread, NULL, read_thread, dev);
-
- /* Wait here for the read thread to be initialized. */
- pthread_barrier_wait(&dev->barrier);
-
- return dev;
- }
- else {
- goto return_error;
- }
+ /* Get the IORegistry entry for the given path */
+ if (strncmp("DevSrvsID:", path, 10) == 0) {
+ char *endptr;
+ uint64_t entry_id = strtoull(path + 10, &endptr, 10);
+ if (*endptr == '\0') {
+ return IOServiceGetMatchingService((mach_port_t) 0, IORegistryEntryIDMatching(entry_id));
}
}
-
+ else {
+ /* Fallback to older format of the path */
+ return IORegistryEntryFromPath((mach_port_t) 0, path);
+ }
+
+ return MACH_PORT_NULL;
+}
+
+hid_device * HID_API_EXPORT hid_open_path(const char *path)
+{
+ hid_device *dev = NULL;
+ io_registry_entry_t entry = MACH_PORT_NULL;
+ IOReturn ret = kIOReturnInvalid;
+ char str[32];
+
+ /* Set up the HID Manager if it hasn't been done */
+ if (hid_init() < 0) {
+ return NULL;
+ }
+ /* register_global_error: global error is set/reset by hid_init */
+
+ dev = new_hid_device();
+ if (!dev) {
+ register_global_error("Couldn't allocate memory");
+ return NULL;
+ }
+
+ /* Get the IORegistry entry for the given path */
+ entry = hid_open_service_registry_from_path(path);
+ if (entry == MACH_PORT_NULL) {
+ /* Path wasn't valid (maybe device was removed?) */
+ register_global_error("hid_open_path: device mach entry not found with the given path");
+ goto return_error;
+ }
+
+ /* Create an IOHIDDevice for the entry */
+ dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry);
+ if (dev->device_handle == NULL) {
+ /* Error creating the HID device */
+ register_global_error("hid_open_path: failed to create IOHIDDevice from the mach entry");
+ goto return_error;
+ }
+
+ /* Open the IOHIDDevice */
+ ret = IOHIDDeviceOpen(dev->device_handle, dev->open_options);
+ if (ret != kIOReturnSuccess) {
+ register_global_error_format("hid_open_path: failed to open IOHIDDevice from mach entry: (0x%08X) %s", ret, mach_error_string(ret));
+ goto return_error;
+ }
+
+ /* Create the buffers for receiving data */
+ dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle);
+ dev->input_report_buf = (uint8_t*) calloc(dev->max_input_report_len, sizeof(uint8_t));
+
+ /* Create the Run Loop Mode for this device.
+ printing the reference seems to work. */
+ snprintf(str, sizeof(str), "HIDAPI_%p", (void*) dev->device_handle);
+ dev->run_loop_mode =
+ CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
+
+ /* Attach the device to a Run Loop */
+ IOHIDDeviceRegisterInputReportCallback(
+ dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
+ &hid_report_callback, dev);
+ IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev);
+
+ /* Start the read thread */
+ pthread_create(&dev->thread, NULL, read_thread, dev);
+
+ /* Wait here for the read thread to be initialized. */
+ pthread_barrier_wait(&dev->barrier);
+
+ IOObjectRelease(entry);
+ return dev;
+
return_error:
- free(device_array);
- CFRelease(device_set);
+ if (dev->device_handle != NULL)
+ CFRelease(dev->device_handle);
+
+ if (entry != MACH_PORT_NULL)
+ IOObjectRelease(entry);
+
free_hid_device(dev);
return NULL;
}
static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length)
{
- const char *pass_through_magic = "MAGIC0";
- size_t pass_through_magic_length = strlen(pass_through_magic);
- unsigned char report_id = data[0];
- const unsigned char *data_to_send;
- size_t length_to_send;
+ const unsigned char *data_to_send = data;
+ CFIndex length_to_send = length;
IOReturn res;
-
- /* Return if the device has been disconnected. */
- if (dev->disconnected)
- return -1;
-
+ unsigned char report_id;
+
+ register_device_error(dev, NULL);
+
+ if (!data || (length == 0)) {
+ register_device_error(dev, strerror(EINVAL));
+ return -1;
+ }
+
+ report_id = data[0];
+
if (report_id == 0x0) {
/* Not using numbered Reports.
- Don't send the report number. */
+ Don't send the report number. */
data_to_send = data+1;
length_to_send = length-1;
}
- else if (length > 6 && memcmp(data, pass_through_magic, pass_through_magic_length) == 0) {
- report_id = data[pass_through_magic_length];
- data_to_send = data+pass_through_magic_length;
- length_to_send = length-pass_through_magic_length;
+
+ /* Avoid crash if the device has been unplugged. */
+ if (dev->disconnected) {
+ register_device_error(dev, "Device is disconnected");
+ return -1;
}
- else {
- /* Using numbered Reports.
- Send the Report Number */
- data_to_send = data;
- length_to_send = length;
+
+ res = IOHIDDeviceSetReport(dev->device_handle,
+ type,
+ report_id,
+ data_to_send, length_to_send);
+
+ if (res != kIOReturnSuccess) {
+ register_device_error_format(dev, "IOHIDDeviceSetReport failed: (0x%08X) %s", res, mach_error_string(res));
+ return -1;
}
-
- if (!dev->disconnected) {
- res = IOHIDDeviceSetReport(dev->device_handle,
- type,
- report_id, /* Report ID*/
- data_to_send, length_to_send);
-
- if (res == kIOReturnSuccess) {
- return (int)length;
- }
- else if (res == kIOReturnUnsupported) {
- /*printf("kIOReturnUnsupported\n");*/
- return -1;
- }
- else {
- /*printf("0x%x\n", res);*/
- return -1;
- }
+
+ return (int) length;
+}
+
+static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data, size_t length)
+{
+ unsigned char *report = data;
+ CFIndex report_length = length;
+ IOReturn res = kIOReturnSuccess;
+ const unsigned char report_id = data[0];
+
+ register_device_error(dev, NULL);
+
+ if (report_id == 0x0) {
+ /* Not using numbered Reports.
+ Don't send the report number. */
+ report = data+1;
+ report_length = length-1;
}
-
- return -1;
+
+ /* Avoid crash if the device has been unplugged. */
+ if (dev->disconnected) {
+ register_device_error(dev, "Device is disconnected");
+ return -1;
+ }
+
+ res = IOHIDDeviceGetReport(dev->device_handle,
+ type,
+ report_id,
+ report, &report_length);
+
+ if (res != kIOReturnSuccess) {
+ register_device_error_format(dev, "IOHIDDeviceGetReport failed: (0x%08X) %s", res, mach_error_string(res));
+ return -1;
+ }
+
+ if (report_id == 0x0) { /* 0 report number still present at the beginning */
+ report_length++;
+ }
+
+ return (int) report_length;
}
int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
@@ -951,90 +1185,91 @@
static int return_data(hid_device *dev, unsigned char *data, size_t length)
{
/* Copy the data out of the linked list item (rpt) into the
- return buffer (data), and delete the liked list item. */
+ return buffer (data), and delete the liked list item. */
struct input_report *rpt = dev->input_reports;
- size_t len = 0;
- if (rpt != NULL) {
- len = (length < rpt->len)? length: rpt->len;
- memcpy(data, rpt->data, len);
- dev->input_reports = rpt->next;
- free(rpt->data);
- free(rpt);
- }
- return (int)len;
+ size_t len = (length < rpt->len)? length: rpt->len;
+ memcpy(data, rpt->data, len);
+ dev->input_reports = rpt->next;
+ free(rpt->data);
+ free(rpt);
+ return (int) len;
}
-static int cond_wait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex)
+static int cond_wait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex)
{
while (!dev->input_reports) {
int res = pthread_cond_wait(cond, mutex);
if (res != 0)
return res;
-
+
/* A res of 0 means we may have been signaled or it may
- be a spurious wakeup. Check to see that there's acutally
- data in the queue before returning, and if not, go back
- to sleep. See the pthread_cond_timedwait() man page for
- details. */
-
- if (dev->shutdown_thread || dev->disconnected)
+ be a spurious wakeup. Check to see that there's actually
+ data in the queue before returning, and if not, go back
+ to sleep. See the pthread_cond_timedwait() man page for
+ details. */
+
+ if (dev->shutdown_thread || dev->disconnected) {
return -1;
+ }
}
-
+
return 0;
}
-static int cond_timedwait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
+static int cond_timedwait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
{
while (!dev->input_reports) {
int res = pthread_cond_timedwait(cond, mutex, abstime);
if (res != 0)
return res;
-
+
/* A res of 0 means we may have been signaled or it may
- be a spurious wakeup. Check to see that there's acutally
- data in the queue before returning, and if not, go back
- to sleep. See the pthread_cond_timedwait() man page for
- details. */
-
- if (dev->shutdown_thread || dev->disconnected)
+ be a spurious wakeup. Check to see that there's actually
+ data in the queue before returning, and if not, go back
+ to sleep. See the pthread_cond_timedwait() man page for
+ details. */
+
+ if (dev->shutdown_thread || dev->disconnected) {
return -1;
+ }
}
-
+
return 0;
-
+
}
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
{
int bytes_read = -1;
-
+
/* Lock the access to the report list. */
pthread_mutex_lock(&dev->mutex);
-
+
/* There's an input report queued up. Return it. */
if (dev->input_reports) {
/* Return the first one */
bytes_read = return_data(dev, data, length);
goto ret;
}
-
+
/* Return if the device has been disconnected. */
if (dev->disconnected) {
bytes_read = -1;
+ register_device_error(dev, "hid_read_timeout: device disconnected");
goto ret;
}
-
+
if (dev->shutdown_thread) {
/* This means the device has been closed (or there
- has been an error. An error code of -1 should
- be returned. */
+ has been an error. An error code of -1 should
+ be returned. */
bytes_read = -1;
+ register_device_error(dev, "hid_read_timeout: thread shutdown");
goto ret;
}
-
+
/* There is no data. Go to sleep and wait for data. */
-
+
if (milliseconds == -1) {
/* Blocking */
int res;
@@ -1043,6 +1278,7 @@
bytes_read = return_data(dev, data, length);
else {
/* There was an error, or a device disconnection. */
+ register_device_error(dev, "hid_read_timeout: error waiting for more data");
bytes_read = -1;
}
}
@@ -1059,20 +1295,22 @@
ts.tv_sec++;
ts.tv_nsec -= 1000000000L;
}
-
+
res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts);
- if (res == 0)
+ if (res == 0) {
bytes_read = return_data(dev, data, length);
- else if (res == ETIMEDOUT)
+ } else if (res == ETIMEDOUT) {
bytes_read = 0;
- else
+ } else {
+ register_device_error(dev, "hid_read_timeout: error waiting for more data");
bytes_read = -1;
+ }
}
else {
/* Purely non-blocking */
bytes_read = 0;
}
-
+
ret:
/* Unlock */
pthread_mutex_unlock(&dev->mutex);
@@ -1088,7 +1326,7 @@
{
/* All Nonblocking operation is handled by the library. */
dev->blocking = !nonblock;
-
+
return 0;
}
@@ -1099,180 +1337,215 @@
int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
{
- CFIndex len = length;
- IOReturn res;
- int skipped_report_id = 0, report_number;
-
- /* Return if the device has been unplugged. */
- if (dev->disconnected)
- return -1;
-
- report_number = data[0];
- if (report_number == 0x0) {
- /* Offset the return buffer by 1, so that the report ID
- will remain in byte 0. */
- data++;
- len--;
- skipped_report_id = 1;
- }
-
- res = IOHIDDeviceGetReport(dev->device_handle,
- kIOHIDReportTypeFeature,
- report_number, /* Report ID */
- data, &len);
- if (res != kIOReturnSuccess)
- return -1;
-
- if (skipped_report_id)
- len++;
-
- return (int)len;
+ return get_report(dev, kIOHIDReportTypeFeature, data, length);
}
+int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
+{
+ return get_report(dev, kIOHIDReportTypeInput, data, length);
+}
void HID_API_EXPORT hid_close(hid_device *dev)
{
if (!dev)
return;
-
- /* Disconnect the report callback before close. */
- if (!dev->disconnected) {
+
+ /* Disconnect the report callback before close.
+ See comment below.
+ */
+ if (is_macos_10_10_or_greater || !dev->disconnected) {
IOHIDDeviceRegisterInputReportCallback(
- dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
- NULL, dev);
+ dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
+ NULL, dev);
+ IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev);
IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode);
IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
}
-
+
/* Cause read_thread() to stop. */
dev->shutdown_thread = 1;
-
+
/* Wake up the run thread's event loop so that the thread can exit. */
CFRunLoopSourceSignal(dev->source);
CFRunLoopWakeUp(dev->run_loop);
-
+
/* Notify the read thread that it can shut down now. */
pthread_barrier_wait(&dev->shutdown_barrier);
-
+
/* Wait for read_thread() to end. */
pthread_join(dev->thread, NULL);
-
+
/* Close the OS handle to the device, but only if it's not
- been unplugged. If it's been unplugged, then calling
- IOHIDDeviceClose() will crash. */
- if (!dev->disconnected) {
- IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeNone);
+ been unplugged. If it's been unplugged, then calling
+ IOHIDDeviceClose() will crash.
+
+ UPD: The crash part was true in/until some version of macOS.
+ Starting with macOS 10.15, there is an opposite effect in some environments:
+ crash happenes if IOHIDDeviceClose() is not called.
+ Not leaking a resource in all tested environments.
+ */
+ if (is_macos_10_10_or_greater || !dev->disconnected) {
+ IOHIDDeviceClose(dev->device_handle, dev->open_options);
}
-
+
/* Clear out the queue of received reports. */
pthread_mutex_lock(&dev->mutex);
while (dev->input_reports) {
return_data(dev, NULL, 0);
}
pthread_mutex_unlock(&dev->mutex);
-
+ CFRelease(dev->device_handle);
+
free_hid_device(dev);
}
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
- return get_manufacturer_string(dev->device_handle, string, maxlen);
+ if (!string || !maxlen)
+ {
+ register_device_error(dev, "Zero buffer/length");
+ return -1;
+ }
+
+ struct hid_device_info *info = hid_get_device_info(dev);
+ if (!info)
+ {
+ // hid_get_device_info will have set an error already
+ return -1;
+ }
+
+ wcsncpy(string, info->manufacturer_string, maxlen);
+ string[maxlen - 1] = L'\0';
+
+ return 0;
}
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
- return get_product_string(dev->device_handle, string, maxlen);
+ if (!string || !maxlen) {
+ register_device_error(dev, "Zero buffer/length");
+ return -1;
+ }
+
+ struct hid_device_info *info = hid_get_device_info(dev);
+ if (!info) {
+ // hid_get_device_info will have set an error already
+ return -1;
+ }
+
+ wcsncpy(string, info->product_string, maxlen);
+ string[maxlen - 1] = L'\0';
+
+ return 0;
}
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
- return get_serial_number(dev->device_handle, string, maxlen);
+ if (!string || !maxlen) {
+ register_device_error(dev, "Zero buffer/length");
+ return -1;
+ }
+
+ struct hid_device_info *info = hid_get_device_info(dev);
+ if (!info) {
+ // hid_get_device_info will have set an error already
+ return -1;
+ }
+
+ wcsncpy(string, info->serial_number, maxlen);
+ string[maxlen - 1] = L'\0';
+
+ return 0;
+}
+
+HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev) {
+ if (!dev->device_info) {
+ dev->device_info = create_device_info(dev->device_handle);
+ if (!dev->device_info) {
+ register_device_error(dev, "Failed to create hid_device_info");
+ }
+ }
+
+ return dev->device_info;
}
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
{
- // TODO:
-
- return 0;
+ (void) dev;
+ (void) string_index;
+ (void) string;
+ (void) maxlen;
+
+ register_device_error(dev, "hid_get_indexed_string: not available on this platform");
+ return -1;
}
+int HID_API_EXPORT_CALL hid_darwin_get_location_id(hid_device *dev, uint32_t *location_id)
+{
+ int res = get_int_property(dev->device_handle, CFSTR(kIOHIDLocationIDKey));
+ if (res != 0) {
+ *location_id = (uint32_t) res;
+ return 0;
+ } else {
+ register_device_error(dev, "Failed to get IOHIDLocationID property");
+ return -1;
+ }
+}
+
+void HID_API_EXPORT_CALL hid_darwin_set_open_exclusive(int open_exclusive)
+{
+ device_open_options = (open_exclusive == 0) ? kIOHIDOptionsTypeNone : kIOHIDOptionsTypeSeizeDevice;
+}
+
+int HID_API_EXPORT_CALL hid_darwin_get_open_exclusive(void)
+{
+ return (device_open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0;
+}
+
+int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev)
+{
+ if (!dev)
+ return -1;
+
+ return (dev->open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0;
+}
+
+int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
+{
+ CFTypeRef ref = IOHIDDeviceGetProperty(dev->device_handle, CFSTR(kIOHIDReportDescriptorKey));
+ if (ref != NULL && CFGetTypeID(ref) == CFDataGetTypeID()) {
+ CFDataRef report_descriptor = (CFDataRef) ref;
+ const UInt8 *descriptor_buf = CFDataGetBytePtr(report_descriptor);
+ CFIndex descriptor_buf_len = CFDataGetLength(report_descriptor);
+ size_t copy_len = (size_t) descriptor_buf_len;
+
+ if (descriptor_buf == NULL || descriptor_buf_len < 0) {
+ register_device_error(dev, "Zero buffer/length");
+ return -1;
+ }
+
+ if (buf_size < copy_len) {
+ copy_len = buf_size;
+ }
+
+ memcpy(buf, descriptor_buf, copy_len);
+ return copy_len;
+ }
+ else {
+ register_device_error(dev, "Failed to get kIOHIDReportDescriptorKey property");
+ return -1;
+ }
+}
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{
- // TODO:
-
- return NULL;
-}
-
-
-
-
-
-
-#if 0
-static int32_t get_location_id(IOHIDDeviceRef device)
-{
- return get_int_property(device, CFSTR(kIOHIDLocationIDKey));
-}
-
-static int32_t get_usage(IOHIDDeviceRef device)
-{
- int32_t res;
- res = get_int_property(device, CFSTR(kIOHIDDeviceUsageKey));
- if (!res)
- res = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey));
- return res;
-}
-
-static int32_t get_usage_page(IOHIDDeviceRef device)
-{
- int32_t res;
- res = get_int_property(device, CFSTR(kIOHIDDeviceUsagePageKey));
- if (!res)
- res = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey));
- return res;
-}
-
-static int get_transport(IOHIDDeviceRef device, wchar_t *buf, size_t len)
-{
- return get_string_property(device, CFSTR(kIOHIDTransportKey), buf, len);
-}
-
-
-int main(void)
-{
- IOHIDManagerRef mgr;
- int i;
-
- mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
- IOHIDManagerSetDeviceMatching(mgr, NULL);
- IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone);
-
- CFSetRef device_set = IOHIDManagerCopyDevices(mgr);
-
- CFIndex num_devices = CFSetGetCount(device_set);
- IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef));
- CFSetGetValues(device_set, (const void **) device_array);
-
- for (i = 0; i < num_devices; i++) {
- IOHIDDeviceRef dev = device_array[i];
- printf("Device: %p\n", dev);
- printf(" %04hx %04hx\n", get_vendor_id(dev), get_product_id(dev));
-
- wchar_t serial[256], buf[256];
- char cbuf[256];
- get_serial_number(dev, serial, 256);
-
-
- printf(" Serial: %ls\n", serial);
- printf(" Loc: %ld\n", get_location_id(dev));
- get_transport(dev, buf, 256);
- printf(" Trans: %ls\n", buf);
- make_path(dev, cbuf, 256);
- printf(" Path: %s\n", cbuf);
-
+ if (dev) {
+ if (dev->last_error_str == NULL)
+ return L"Success";
+ return dev->last_error_str;
}
-
- return 0;
+
+ if (last_global_error_str == NULL)
+ return L"Success";
+ return last_global_error_str;
}
-#endif
diff --git a/src/hidapi/mac/hidapi_darwin.h b/src/hidapi/mac/hidapi_darwin.h
new file mode 100644
index 0000000..34c30a0
--- /dev/null
+++ b/src/hidapi/mac/hidapi_darwin.h
@@ -0,0 +1,98 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ libusb/hidapi Team
+
+ Copyright 2022, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+ https://github.com/libusb/hidapi .
+********************************************************/
+
+/** @file
+ * @defgroup API hidapi API
+
+ * Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+ */
+
+#ifndef HIDAPI_DARWIN_H__
+#define HIDAPI_DARWIN_H__
+
+#include <stdint.h>
+
+#include "hidapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /** @brief Get the location ID for a HID device.
+
+ Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+
+ @ingroup API
+ @param dev A device handle returned from hid_open().
+ @param location_id The device's location ID on return.
+
+ @returns
+ This function returns 0 on success and -1 on error.
+ */
+ int HID_API_EXPORT_CALL hid_darwin_get_location_id(hid_device *dev, uint32_t *location_id);
+
+
+ /** @brief Changes the behavior of all further calls to @ref hid_open or @ref hid_open_path.
+
+ By default on Darwin platform all devices opened by HIDAPI with @ref hid_open or @ref hid_open_path
+ are opened in exclusive mode (see kIOHIDOptionsTypeSeizeDevice).
+
+ Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+
+ @ingroup API
+ @param open_exclusive When set to 0 - all further devices will be opened
+ in non-exclusive mode. Otherwise - all further devices will be opened
+ in exclusive mode.
+
+ @note During the initialisation by @ref hid_init - this property is set to 1 (TRUE).
+ This is done to preserve full backward compatibility with previous behavior.
+
+ @note Calling this function before @ref hid_init or after @ref hid_exit has no effect.
+ */
+ void HID_API_EXPORT_CALL hid_darwin_set_open_exclusive(int open_exclusive);
+
+ /** @brief Getter for option set by @ref hid_darwin_set_open_exclusive.
+
+ Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+
+ @ingroup API
+ @return 1 if all further devices will be opened in exclusive mode.
+
+ @note Value returned by this function before calling to @ref hid_init or after @ref hid_exit
+ is not reliable.
+ */
+ int HID_API_EXPORT_CALL hid_darwin_get_open_exclusive(void);
+
+ /** @brief Check how the device was opened.
+
+ Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+
+ @ingroup API
+ @param dev A device to get property from.
+
+ @return 1 if the device is opened in exclusive mode, 0 - opened in non-exclusive,
+ -1 - if dev is invalid.
+ */
+ int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/hidapi/meson.build b/src/hidapi/meson.build
new file mode 100644
index 0000000..d7867cb
--- /dev/null
+++ b/src/hidapi/meson.build
@@ -0,0 +1,22 @@
+project('hidapi', meson_version: '>=0.57.0', version: files('VERSION'))
+
+cmake = import('cmake')
+
+hidapi_build_options = cmake.subproject_options()
+hidapi_build_options.set_install(true)
+
+hidapi_build = cmake.subproject('hidapi_build_cmake', options: hidapi_build_options)
+
+if (hidapi_build.target_list().contains('hidapi_winapi'))
+ hidapi_winapi_dep = hidapi_build.dependency('hidapi_winapi')
+ hidapi_dep = hidapi_winapi_dep
+elif (hidapi_build.target_list().contains('hidapi_darwin'))
+ hidapi_darwin_dep = hidapi_build.dependency('hidapi_darwin')
+ hidapi_dep = hidapi_darwin_dep
+elif (hidapi_build.target_list().contains('hidapi_hidraw'))
+ hidapi_hidraw_dep = hidapi_build.dependency('hidapi_hidraw')
+ hidapi_dep = hidapi_hidraw_dep
+elif (hidapi_build.target_list().contains('hidapi_libusb'))
+ hidapi_libusb_dep = hidapi_build.dependency('hidapi_libusb')
+ hidapi_dep = hidapi_libusb_dep
+endif
diff --git a/src/hidapi/pc/.gitignore b/src/hidapi/pc/.gitignore
new file mode 100644
index 0000000..6fd0ef0
--- /dev/null
+++ b/src/hidapi/pc/.gitignore
@@ -0,0 +1 @@
+*.pc
diff --git a/src/hidapi/pc/hidapi-hidraw.pc.in b/src/hidapi/pc/hidapi-hidraw.pc.in
index e20558d..28b9fe6 100644
--- a/src/hidapi/pc/hidapi-hidraw.pc.in
+++ b/src/hidapi/pc/hidapi-hidraw.pc.in
@@ -5,6 +5,7 @@
Name: hidapi-hidraw
Description: C Library for USB/Bluetooth HID device access from Linux, Mac OS X, FreeBSD, and Windows. This is the hidraw implementation.
+URL: https://github.com/libusb/hidapi
Version: @VERSION@
Libs: -L${libdir} -lhidapi-hidraw
Cflags: -I${includedir}/hidapi
diff --git a/src/hidapi/pc/hidapi-libusb.pc.in b/src/hidapi/pc/hidapi-libusb.pc.in
index 2e49506..d51e98f 100644
--- a/src/hidapi/pc/hidapi-libusb.pc.in
+++ b/src/hidapi/pc/hidapi-libusb.pc.in
@@ -5,6 +5,7 @@
Name: hidapi-libusb
Description: C Library for USB HID device access from Linux, Mac OS X, FreeBSD, and Windows. This is the libusb implementation.
+URL: https://github.com/libusb/hidapi
Version: @VERSION@
Libs: -L${libdir} -lhidapi-libusb
Cflags: -I${includedir}/hidapi
diff --git a/src/hidapi/pc/hidapi.pc.in b/src/hidapi/pc/hidapi.pc.in
index 5835c99..75b11a5 100644
--- a/src/hidapi/pc/hidapi.pc.in
+++ b/src/hidapi/pc/hidapi.pc.in
@@ -5,6 +5,7 @@
Name: hidapi
Description: C Library for USB/Bluetooth HID device access from Linux, Mac OS X, FreeBSD, and Windows.
+URL: https://github.com/libusb/hidapi
Version: @VERSION@
Libs: -L${libdir} -lhidapi
Cflags: -I${includedir}/hidapi
diff --git a/src/hidapi/src/CMakeLists.txt b/src/hidapi/src/CMakeLists.txt
new file mode 100644
index 0000000..d08224b
--- /dev/null
+++ b/src/hidapi/src/CMakeLists.txt
@@ -0,0 +1,193 @@
+get_filename_component(PROJECT_ROOT "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
+
+# Read version from file
+file(READ "${PROJECT_ROOT}/VERSION" RAW_VERSION_STR)
+string(REGEX MATCH "^([0-9]+\\.[0-9]+\\.[0-9]+)(.*)" VERSION_STR "${RAW_VERSION_STR}")
+
+if(NOT VERSION_STR)
+ message(FATAL_ERROR "Broken VERSION file, couldn't parse '${PROJECT_ROOT}/VERSION' with content: '${RAW_VERSION_STR}'")
+endif()
+
+set(VERSION "${CMAKE_MATCH_1}")
+string(STRIP "${CMAKE_MATCH_2}" VERSION_SUFFIX)
+# compatibility with find_package() vs add_subdirectory
+set(hidapi_VERSION "${VERSION}" PARENT_SCOPE)
+#
+
+if(DEFINED HIDAPI_PRINT_VERSION AND HIDAPI_PRINT_VERSION)
+ set(HIDAPI_PRINT_VERSION "hidapi: v${VERSION}")
+ if(VERSION_SUFFIX)
+ set(HIDAPI_PRINT_VERSION "${HIDAPI_PRINT_VERSION} (${VERSION_SUFFIX})")
+ endif()
+ message(STATUS "${HIDAPI_PRINT_VERSION}")
+endif()
+
+project(hidapi VERSION "${VERSION}" LANGUAGES C)
+
+# Defaults and required options
+
+if(NOT DEFINED HIDAPI_WITH_TESTS)
+ set(HIDAPI_WITH_TESTS OFF)
+endif()
+if(NOT DEFINED BUILD_SHARED_LIBS)
+ set(BUILD_SHARED_LIBS ON)
+endif()
+if(NOT DEFINED HIDAPI_INSTALL_TARGETS)
+ set(HIDAPI_INSTALL_TARGETS OFF)
+endif()
+if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+endif()
+
+get_directory_property(IS_EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL)
+if(IS_EXCLUDE_FROM_ALL)
+ if(HIDAPI_INSTALL_TARGETS)
+ message(WARNING "Installing EXCLUDE_FROM_ALL targets in an undefined behavior in CMake.\nDon't add 'hidapi' sundirectory with 'EXCLUDE_FROM_ALL' property, or don't set 'HIDAPI_INSTALL_TARGETS' to TRUE.")
+ endif()
+endif()
+
+# Helper(s)
+
+function(hidapi_configure_pc PC_IN_FILE)
+ file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/pc")
+
+ set(VERSION "${VERSION}${VERSION_SUFFIX}")
+ set(prefix "${CMAKE_INSTALL_PREFIX}")
+ set(exec_prefix "\${prefix}")
+ if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
+ set(libdir "${CMAKE_INSTALL_LIBDIR}")
+ else()
+ set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
+ endif()
+ if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
+ set(includedir "${CMAKE_INSTALL_INCLUDEDIR}")
+ else()
+ set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
+ endif()
+
+ get_filename_component(PC_IN_FILENAME "${PC_IN_FILE}" NAME_WE)
+ set(PC_FILE "${CMAKE_CURRENT_BINARY_DIR}/pc/${PC_IN_FILENAME}.pc")
+ configure_file("${PC_IN_FILE}" "${PC_FILE}" @ONLY)
+
+ install(FILES "${PC_FILE}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig/")
+endfunction()
+
+# The library
+
+if(HIDAPI_INSTALL_TARGETS)
+ include(GNUInstallDirs)
+endif()
+
+add_library(hidapi_include INTERFACE)
+target_include_directories(hidapi_include INTERFACE
+ "$<BUILD_INTERFACE:${PROJECT_ROOT}/hidapi>"
+)
+if(APPLE AND CMAKE_FRAMEWORK)
+ # FIXME: https://github.com/libusb/hidapi/issues/492: it is untrivial to set the include path for Framework correctly
+else()
+ target_include_directories(hidapi_include INTERFACE
+ "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/hidapi>"
+ )
+endif()
+set_target_properties(hidapi_include PROPERTIES EXPORT_NAME "include")
+set(HIDAPI_PUBLIC_HEADERS "${PROJECT_ROOT}/hidapi/hidapi.h")
+
+add_library(hidapi::include ALIAS hidapi_include)
+
+if(HIDAPI_INSTALL_TARGETS)
+ install(TARGETS hidapi_include EXPORT hidapi)
+endif()
+
+set(EXPORT_ALIAS)
+set(EXPORT_COMPONENTS)
+
+set(HIDAPI_NEED_EXPORT_THREADS FALSE)
+set(HIDAPI_NEED_EXPORT_LIBUSB FALSE)
+set(HIDAPI_NEED_EXPORT_LIBUDEV FALSE)
+set(HIDAPI_NEED_EXPORT_ICONV FALSE)
+
+if(WIN32)
+ target_include_directories(hidapi_include INTERFACE
+ "$<BUILD_INTERFACE:${PROJECT_ROOT}/windows>"
+ )
+ add_subdirectory("${PROJECT_ROOT}/windows" windows)
+ set(EXPORT_ALIAS winapi)
+ list(APPEND EXPORT_COMPONENTS winapi)
+elseif(APPLE)
+ target_include_directories(hidapi_include INTERFACE
+ "$<BUILD_INTERFACE:${PROJECT_ROOT}/mac>"
+ )
+ add_subdirectory("${PROJECT_ROOT}/mac" mac)
+ set(EXPORT_ALIAS darwin)
+ list(APPEND EXPORT_COMPONENTS darwin)
+ if(NOT BUILD_SHARED_LIBS)
+ set(HIDAPI_NEED_EXPORT_THREADS TRUE)
+ endif()
+else()
+ if(NOT DEFINED HIDAPI_WITH_LIBUSB)
+ set(HIDAPI_WITH_LIBUSB ON)
+ endif()
+ if(CMAKE_SYSTEM_NAME MATCHES "Linux")
+ if(NOT DEFINED HIDAPI_WITH_HIDRAW)
+ set(HIDAPI_WITH_HIDRAW ON)
+ endif()
+ if(HIDAPI_WITH_HIDRAW)
+ add_subdirectory("${PROJECT_ROOT}/linux" linux)
+ list(APPEND EXPORT_COMPONENTS hidraw)
+ set(EXPORT_ALIAS hidraw)
+ if(NOT BUILD_SHARED_LIBS)
+ set(HIDAPI_NEED_EXPORT_THREADS TRUE)
+ set(HIDAPI_NEED_EXPORT_LIBUDEV TRUE)
+ endif()
+ endif()
+ else()
+ set(HIDAPI_WITH_LIBUSB ON)
+ endif()
+ if(HIDAPI_WITH_LIBUSB)
+ target_include_directories(hidapi_include INTERFACE
+ "$<BUILD_INTERFACE:${PROJECT_ROOT}/libusb>"
+ )
+ if(NOT DEFINED HIDAPI_NO_ICONV)
+ set(HIDAPI_NO_ICONV OFF)
+ endif()
+ add_subdirectory("${PROJECT_ROOT}/libusb" libusb)
+ list(APPEND EXPORT_COMPONENTS libusb)
+ if(NOT EXPORT_ALIAS)
+ set(EXPORT_ALIAS libusb)
+ endif()
+ if(NOT BUILD_SHARED_LIBS)
+ set(HIDAPI_NEED_EXPORT_THREADS TRUE)
+ if(NOT TARGET usb-1.0)
+ set(HIDAPI_NEED_EXPORT_LIBUSB TRUE)
+ endif()
+ endif()
+ elseif(NOT TARGET hidapi_hidraw)
+ message(FATAL_ERROR "Select at least one option to build: HIDAPI_WITH_LIBUSB or HIDAPI_WITH_HIDRAW")
+ endif()
+endif()
+
+add_library(hidapi::hidapi ALIAS hidapi_${EXPORT_ALIAS})
+
+if(HIDAPI_INSTALL_TARGETS)
+ include(CMakePackageConfigHelpers)
+ set(EXPORT_DENERATED_LOCATION "${CMAKE_BINARY_DIR}/export_generated")
+ set(EXPORT_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/hidapi")
+ write_basic_package_version_file("${EXPORT_DENERATED_LOCATION}/hidapi-config-version.cmake"
+ COMPATIBILITY SameMajorVersion
+ )
+ configure_package_config_file("cmake/hidapi-config.cmake.in" "${EXPORT_DENERATED_LOCATION}/hidapi-config.cmake"
+ INSTALL_DESTINATION "${EXPORT_DESTINATION}"
+ NO_SET_AND_CHECK_MACRO
+ )
+
+ install(EXPORT hidapi
+ DESTINATION "${EXPORT_DESTINATION}"
+ NAMESPACE hidapi::
+ FILE "libhidapi.cmake"
+ )
+ install(FILES
+ "${EXPORT_DENERATED_LOCATION}/hidapi-config-version.cmake"
+ "${EXPORT_DENERATED_LOCATION}/hidapi-config.cmake"
+ DESTINATION "${EXPORT_DESTINATION}"
+ )
+endif()
diff --git a/src/hidapi/src/cmake/hidapi-config.cmake.in b/src/hidapi/src/cmake/hidapi-config.cmake.in
new file mode 100644
index 0000000..4226c8c
--- /dev/null
+++ b/src/hidapi/src/cmake/hidapi-config.cmake.in
@@ -0,0 +1,61 @@
+@PACKAGE_INIT@
+
+set(hidapi_VERSION_MAJOR "@hidapi_VERSION_MAJOR@")
+set(hidapi_VERSION_MINOR "@hidapi_VERSION_MINOR@")
+set(hidapi_VERSION_PATCH "@hidapi_VERSION_PATCH@")
+set(hidapi_VERSION "@hidapi_VERSION@")
+set(hidapi_VERSION_STR "@hidapi_VERSION@@VERSION_SUFFIX@")
+
+set(hidapi_FOUND FALSE)
+
+set(HIDAPI_NEED_EXPORT_THREADS @HIDAPI_NEED_EXPORT_THREADS@)
+set(HIDAPI_NEED_EXPORT_LIBUSB @HIDAPI_NEED_EXPORT_LIBUSB@)
+set(HIDAPI_NEED_EXPORT_LIBUDEV @HIDAPI_NEED_EXPORT_LIBUDEV@)
+set(HIDAPI_NEED_EXPORT_ICONV @HIDAPI_NEED_EXPORT_ICONV@)
+
+if(HIDAPI_NEED_EXPORT_THREADS)
+ if(CMAKE_VERSION VERSION_LESS 3.4.3)
+ message(FATAL_ERROR "This file relies on consumers using CMake 3.4.3 or greater.")
+ endif()
+ find_package(Threads REQUIRED)
+endif()
+
+if(HIDAPI_NEED_EXPORT_LIBUSB OR HIDAPI_NEED_EXPORT_LIBUDEV)
+ if(CMAKE_VERSION VERSION_LESS 3.6.3)
+ message(FATAL_ERROR "This file relies on consumers using CMake 3.6.3 or greater.")
+ endif()
+ find_package(PkgConfig)
+ if(HIDAPI_NEED_EXPORT_LIBUSB)
+ pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0>=1.0.9)
+ endif()
+ if(HIDAPI_NEED_EXPORT_LIBUDEV)
+ pkg_check_modules(libudev REQUIRED IMPORTED_TARGET libudev)
+ endif()
+endif()
+
+if(HIDAPI_NEED_EXPORT_ICONV)
+ if(CMAKE_VERSION VERSION_LESS 3.11)
+ message(WARNING "HIDAPI requires CMake target Iconv::Iconv, make sure to provide it")
+ else()
+ find_package(Iconv REQUIRED)
+ endif()
+endif()
+
+include("${CMAKE_CURRENT_LIST_DIR}/libhidapi.cmake")
+
+set(hidapi_FOUND TRUE)
+
+foreach(_component @EXPORT_COMPONENTS@)
+ if(TARGET hidapi::${_component})
+ set(hidapi_${_component}_FOUND TRUE)
+ endif()
+endforeach()
+
+check_required_components(hidapi)
+
+if(NOT TARGET hidapi::hidapi)
+ add_library(hidapi::hidapi INTERFACE IMPORTED)
+ set_target_properties(hidapi::hidapi PROPERTIES
+ INTERFACE_LINK_LIBRARIES hidapi::@EXPORT_ALIAS@
+ )
+endif()
diff --git a/src/hidapi/subprojects/README.md b/src/hidapi/subprojects/README.md
new file mode 100644
index 0000000..8ba8f60
--- /dev/null
+++ b/src/hidapi/subprojects/README.md
@@ -0,0 +1,2 @@
+This folder is used only to support [meson.build](../meson.build) `subproject` command
+which would only look for a subproject in a "subprojects" directory.
\ No newline at end of file
diff --git a/src/hidapi/subprojects/hidapi_build_cmake/CMakeLists.txt b/src/hidapi/subprojects/hidapi_build_cmake/CMakeLists.txt
new file mode 100644
index 0000000..80aed67
--- /dev/null
+++ b/src/hidapi/subprojects/hidapi_build_cmake/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR)
+project(hidapi LANGUAGES C)
+
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/root")
+
+foreach(ROOT_ELEMENT CMakeLists.txt hidapi src windows linux mac libusb pc VERSION)
+ file(COPY "${CMAKE_CURRENT_LIST_DIR}/../../${ROOT_ELEMENT}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/root/")
+endforeach()
+
+add_subdirectory("${CMAKE_CURRENT_BINARY_DIR}/root" hidapi_root)
diff --git a/src/hidapi/testgui/.gitignore b/src/hidapi/testgui/.gitignore
new file mode 100644
index 0000000..f989ea8
--- /dev/null
+++ b/src/hidapi/testgui/.gitignore
@@ -0,0 +1,20 @@
+Debug
+Release
+*.exp
+*.ilk
+*.lib
+*.suo
+*.vcproj.*
+*.ncb
+*.suo
+*.dll
+*.pdb
+*.o
+hidapi-testgui
+hidapi-hidraw-testgui
+hidapi-libusb-testgui
+.deps
+.libs
+*.la
+*.lo
+TestGUI.app
diff --git a/src/hidapi/testgui/Makefile.mingw b/src/hidapi/testgui/Makefile.mingw
index df0f69d..f76b5a0 100644
--- a/src/hidapi/testgui/Makefile.mingw
+++ b/src/hidapi/testgui/Makefile.mingw
@@ -14,7 +14,7 @@
CPPOBJS=test.o
OBJS=$(COBJS) $(CPPOBJS)
CFLAGS=-I../hidapi -I../../hidapi-externals/fox/include -g -c
-LIBS= -mwindows -lsetupapi -L../../hidapi-externals/fox/lib -Wl,-Bstatic -lFOX-1.6 -Wl,-Bdynamic -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32
+LIBS= -mwindows -L../../hidapi-externals/fox/lib -Wl,-Bstatic -lFOX-1.6 -Wl,-Bdynamic -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32
hidapi-testgui: $(OBJS)
diff --git a/src/hidapi/testgui/copy_to_bundle.sh b/src/hidapi/testgui/copy_to_bundle.sh
index f0fc767..ddff64f 100755
--- a/src/hidapi/testgui/copy_to_bundle.sh
+++ b/src/hidapi/testgui/copy_to_bundle.sh
@@ -77,9 +77,10 @@
}
rm -f $EXEPATH/*
+mkdir -p $EXEPATH
# Copy the binary into the bundle. Use ../libtool to do this if it's
-# available beacuse if $EXE_NAME was built with autotools, it will be
+# available because if $EXE_NAME was built with autotools, it will be
# necessary. If ../libtool not available, just use cp to do the copy, but
# only if $EXE_NAME is a binary.
if [ -x ../libtool ]; then
diff --git a/src/hidapi/testgui/mac_support.cpp b/src/hidapi/testgui/mac_support.cpp
deleted file mode 100644
index e1e3874..0000000
--- a/src/hidapi/testgui/mac_support.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*******************************
- Mac support for HID Test GUI
-
- Alan Ott
- Signal 11 Software
-
- Some of this code is from Apple Documentation, most notably
- http://developer.apple.com/legacy/mac/library/documentation/AppleScript/Conceptual/AppleEvents/AppleEvents.pdf
-*******************************/
-
-#include <Carbon/Carbon.h>
-#include <fx.h>
-
-
-extern FXMainWindow *g_main_window;
-
-static pascal OSErr HandleQuitMessage(const AppleEvent *theAppleEvent, AppleEvent
- *reply, long handlerRefcon)
-{
- puts("Quitting\n");
- FXApp::instance()->exit();
- return 0;
-}
-
-static pascal OSErr HandleReopenMessage(const AppleEvent *theAppleEvent, AppleEvent
- *reply, long handlerRefcon)
-{
- puts("Showing");
- g_main_window->show();
- return 0;
-}
-
-static pascal OSErr HandleWildCardMessage(const AppleEvent *theAppleEvent, AppleEvent
- *reply, long handlerRefcon)
-{
- puts("WildCard\n");
- return 0;
-}
-
-OSStatus AEHandler(EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon)
-{
- Boolean release = false;
- EventRecord eventRecord;
- OSErr ignoreErrForThisSample;
-
- // Events of type kEventAppleEvent must be removed from the queue
- // before being passed to AEProcessAppleEvent.
- if (IsEventInQueue(GetMainEventQueue(), inEvent))
- {
- // RemoveEventFromQueue will release the event, which will
- // destroy it if we don't retain it first.
- RetainEvent(inEvent);
- release = true;
- RemoveEventFromQueue(GetMainEventQueue(), inEvent);
- }
- // Convert the event ref to the type AEProcessAppleEvent expects.
- ConvertEventRefToEventRecord(inEvent, &eventRecord);
- ignoreErrForThisSample = AEProcessAppleEvent(&eventRecord);
- if (release)
- ReleaseEvent(inEvent);
- // This Carbon event has been handled, even if no AppleEvent handlers
- // were installed for the Apple event.
- return noErr;
-}
-
-static void HandleEvent(EventRecord *event)
-{
- //printf("What: %d message %x\n", event->what, event->message);
- if (event->what == osEvt) {
- if (((event->message >> 24) & 0xff) == suspendResumeMessage) {
- if (event->message & resumeFlag) {
- g_main_window->show();
- }
- }
- }
-
-#if 0
- switch (event->what)
- {
- case mouseDown:
- //HandleMouseDown(event);
- break;
- case keyDown:
- case autoKey:
- //HandleKeyPress(event);
- break;
- case kHighLevelEvent:
- puts("Calling ProcessAppleEvent\n");
- AEProcessAppleEvent(event);
- break;
- }
-#endif
-}
-
-void
-init_apple_message_system()
-{
- OSErr err;
- static const EventTypeSpec appleEvents[] =
- {
- { kEventClassAppleEvent, kEventAppleEvent }
- };
-
- /* Install the handler for Apple Events */
- InstallApplicationEventHandler(NewEventHandlerUPP(AEHandler),
- GetEventTypeCount(appleEvents), appleEvents, 0, NULL);
-
- /* Install handlers for the individual Apple Events that come
- from the Dock icon: the Reopen (click), and the Quit messages. */
- err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
- NewAEEventHandlerUPP(HandleQuitMessage), 0, false);
- err = AEInstallEventHandler(kCoreEventClass, kAEReopenApplication,
- NewAEEventHandlerUPP(HandleReopenMessage), 0, false);
-#if 0
- // Left as an example of a wild card match.
- err = AEInstallEventHandler(kCoreEventClass, typeWildCard,
- NewAEEventHandlerUPP(HandleWildMessage), 0, false);
-#endif
-}
-
-void
-check_apple_events()
-{
- RgnHandle cursorRgn = NULL;
- Boolean gotEvent=TRUE;
- EventRecord event;
-
- while (gotEvent) {
- gotEvent = WaitNextEvent(everyEvent, &event, 0L/*timeout*/, cursorRgn);
- if (gotEvent) {
- HandleEvent(&event);
- }
- }
-}
diff --git a/src/hidapi/testgui/mac_support_cocoa.m b/src/hidapi/testgui/mac_support_cocoa.m
index 75de7e9..1b12163 100644
--- a/src/hidapi/testgui/mac_support_cocoa.m
+++ b/src/hidapi/testgui/mac_support_cocoa.m
@@ -8,10 +8,19 @@
#include <fx.h>
#import <Cocoa/Cocoa.h>
+#ifndef MAC_OS_X_VERSION_10_12
+#define MAC_OS_X_VERSION_10_12 101200
+#endif
+
+// macOS 10.12 deprecated NSAnyEventMask in favor of NSEventMaskAny
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
+#define NSEventMaskAny NSAnyEventMask
+#endif
+
extern FXMainWindow *g_main_window;
-@interface MyAppDelegate : NSObject
+@interface MyAppDelegate : NSObject<NSApplicationDelegate>
{
}
@end
@@ -77,7 +86,7 @@
NSAutoreleasePool *pool = [NSAutoreleasePool new];
while (1) {
- NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:nil
inMode:NSDefaultRunLoopMode
dequeue:YES];
diff --git a/src/hidapi/testgui/start.sh b/src/hidapi/testgui/start.sh
deleted file mode 100755
index 980635d..0000000
--- a/src/hidapi/testgui/start.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-xterm -e /Users/alan/work/hidapi/testgui/TestGUI.app/Contents/MacOS/tg
diff --git a/src/hidapi/testgui/testgui.sln b/src/hidapi/testgui/testgui.sln
index 35abcec..56be6c6 100644
--- a/src/hidapi/testgui/testgui.sln
+++ b/src/hidapi/testgui/testgui.sln
@@ -1,20 +1,20 @@
-
-Microsoft Visual Studio Solution File, Format Version 10.00
-# Visual C++ Express 2008
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testgui", "testgui.vcproj", "{08769AC3-785A-4DDC-BFC7-1775414B7AB7}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Win32 = Debug|Win32
- Release|Win32 = Release|Win32
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Debug|Win32.ActiveCfg = Debug|Win32
- {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Debug|Win32.Build.0 = Debug|Win32
- {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Release|Win32.ActiveCfg = Release|Win32
- {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Release|Win32.Build.0 = Release|Win32
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual C++ Express 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testgui", "testgui.vcproj", "{08769AC3-785A-4DDC-BFC7-1775414B7AB7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Debug|Win32.ActiveCfg = Debug|Win32
+ {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Debug|Win32.Build.0 = Debug|Win32
+ {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Release|Win32.ActiveCfg = Release|Win32
+ {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/hidapi/testgui/testgui.vcproj b/src/hidapi/testgui/testgui.vcproj
index 91be8ee..f6e6c09 100644
--- a/src/hidapi/testgui/testgui.vcproj
+++ b/src/hidapi/testgui/testgui.vcproj
@@ -1,217 +1,217 @@
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="9.00"
- Name="testgui"
- ProjectGUID="{08769AC3-785A-4DDC-BFC7-1775414B7AB7}"
- RootNamespace="testgui"
- Keyword="Win32Proj"
- TargetFrameworkVersion="196613"
- >
- <Platforms>
- <Platform
- Name="Win32"
- />
- </Platforms>
- <ToolFiles>
- </ToolFiles>
- <Configurations>
- <Configuration
- Name="Debug|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
- ConfigurationType="1"
- CharacterSet="1"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- AdditionalIncludeDirectories=""..\..\hidapi-externals\fox\include";..\hidapi"
- PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
- MinimalRebuild="true"
- BasicRuntimeChecks="3"
- RuntimeLibrary="3"
- UsePrecompiledHeader="0"
- WarningLevel="3"
- DebugInformationFormat="4"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="setupapi.lib fox-1.6.lib"
- OutputFile="$(ProjectName).exe"
- LinkIncremental="2"
- AdditionalLibraryDirectories="..\hidapi\objfre_wxp_x86\i386;"..\..\hidapi-externals\fox\lib""
- GenerateDebugInformation="true"
- SubSystem="2"
- EntryPointSymbol="mainCRTStartup"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine=""
- />
- </Configuration>
- <Configuration
- Name="Release|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
- ConfigurationType="1"
- CharacterSet="1"
- WholeProgramOptimization="1"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- EnableIntrinsicFunctions="true"
- AdditionalIncludeDirectories=""..\..\hidapi-externals\fox\include";..\hidapi"
- PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
- RuntimeLibrary="2"
- EnableFunctionLevelLinking="true"
- UsePrecompiledHeader="0"
- WarningLevel="3"
- DebugInformationFormat="3"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="setupapi.lib fox-1.6.lib"
- OutputFile="$(ProjectName).exe"
- LinkIncremental="1"
- AdditionalLibraryDirectories="..\hidapi\objfre_wxp_x86\i386;"..\..\hidapi-externals\fox\lib""
- GenerateDebugInformation="true"
- SubSystem="2"
- OptimizeReferences="2"
- EnableCOMDATFolding="2"
- EntryPointSymbol="mainCRTStartup"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine=""
- />
- </Configuration>
- </Configurations>
- <References>
- </References>
- <Files>
- <Filter
- Name="Source Files"
- Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
- UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
- >
- <File
- RelativePath="..\windows\hid.c"
- >
- </File>
- <File
- RelativePath=".\test.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Header Files"
- Filter="h;hpp;hxx;hm;inl;inc;xsd"
- UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
- >
- <File
- RelativePath="..\hidapi\hidapi.h"
- >
- </File>
- </Filter>
- <Filter
- Name="Resource Files"
- Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
- UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
- >
- </Filter>
- <File
- RelativePath=".\ReadMe.txt"
- >
- </File>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="testgui"
+ ProjectGUID="{08769AC3-785A-4DDC-BFC7-1775414B7AB7}"
+ RootNamespace="testgui"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""..\..\hidapi-externals\fox\include";..\hidapi"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="fox-1.6.lib"
+ OutputFile="$(ProjectName).exe"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="..\hidapi\objfre_wxp_x86\i386;"..\..\hidapi-externals\fox\lib""
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ EntryPointSymbol="mainCRTStartup"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine=""
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories=""..\..\hidapi-externals\fox\include";..\hidapi"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="fox-1.6.lib"
+ OutputFile="$(ProjectName).exe"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="..\hidapi\objfre_wxp_x86\i386;"..\..\hidapi-externals\fox\lib""
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ EntryPointSymbol="mainCRTStartup"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine=""
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\windows\hid.c"
+ >
+ </File>
+ <File
+ RelativePath=".\test.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath="..\hidapi\hidapi.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ <File
+ RelativePath=".\ReadMe.txt"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src/hidapi/udev/69-hid.rules b/src/hidapi/udev/69-hid.rules
new file mode 100644
index 0000000..a27113c
--- /dev/null
+++ b/src/hidapi/udev/69-hid.rules
@@ -0,0 +1,36 @@
+# This is a sample udev file for HIDAPI devices which lets unprivileged
+# users who are physically present at the system (not remote users) access
+# HID devices.
+
+# If you are using the libusb implementation of hidapi (libusb/hid.c), then
+# use something like the following line, substituting the VID and PID with
+# those of your device.
+
+# HIDAPI/libusb
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", TAG+="uaccess"
+
+# If you are using the hidraw implementation (linux/hid.c), then do something
+# like the following, substituting the VID and PID with your device.
+
+# HIDAPI/hidraw
+KERNEL=="hidraw*", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", TAG+="uaccess"
+
+# Once done, optionally rename this file for your application, and drop it into
+# /etc/udev/rules.d/.
+# NOTE: these rules must have priorty before 73-seat-late.rules.
+# (Small discussion/explanation in systemd repo:
+# https://github.com/systemd/systemd/issues/4288#issuecomment-348166161)
+# for example, name the file /etc/udev/rules.d/70-my-application-hid.rules.
+# Then, replug your device or run:
+# sudo udevadm control --reload-rules && sudo udevadm trigger
+
+# Note that the hexadecimal values for VID and PID are case sensitive and
+# must be lower case.
+
+# TAG+="uaccess" only gives permission to physically present users, which
+# is appropriate in most scenarios. If you require access to the device
+# from a remote session (e.g. over SSH), add
+# GROUP="plugdev", MODE="660"
+# to the end of the udev rule lines, add your user to the plugdev group with:
+# usermod -aG plugdev USERNAME
+# then log out and log back in (or restart the system).
diff --git a/src/hidapi/udev/99-hid.rules b/src/hidapi/udev/99-hid.rules
deleted file mode 100644
index 0385f50..0000000
--- a/src/hidapi/udev/99-hid.rules
+++ /dev/null
@@ -1,33 +0,0 @@
-# This is a sample udev file for HIDAPI devices which changes the permissions
-# to 0666 (world readable/writable) for a specified device on Linux systems.
-
-
-# If you are using the libusb implementation of hidapi (libusb/hid.c), then
-# use something like the following line, substituting the VID and PID with
-# those of your device. Note that for kernels before 2.6.24, you will need
-# to substitute "usb" with "usb_device". It shouldn't hurt to use two lines
-# (one each way) for compatibility with older systems.
-
-# HIDAPI/libusb
-SUBSYSTEM=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", MODE="0666"
-
-
-# If you are using the hidraw implementation (linux/hid.c), then do something
-# like the following, substituting the VID and PID with your device. Busnum 1
-# is USB.
-
-# HIDAPI/hidraw
-KERNEL=="hidraw*", ATTRS{busnum}=="1", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", MODE="0666"
-
-# Once done, optionally rename this file for your device, and drop it into
-# /etc/udev/rules.d and unplug and re-plug your device. This is all that is
-# necessary to see the new permissions. Udev does not have to be restarted.
-
-# Note that the hexadecimal values for VID and PID are case sensitive and
-# must be lower case.
-
-# If you think permissions of 0666 are too loose, then see:
-# http://reactivated.net/writing_udev_rules.html for more information on finer
-# grained permission setting. For example, it might be sufficient to just
-# set the group or user owner for specific devices (for example the plugdev
-# group on some systems).
diff --git a/src/hidapi/windows/.gitignore b/src/hidapi/windows/.gitignore
new file mode 100644
index 0000000..c2ad395
--- /dev/null
+++ b/src/hidapi/windows/.gitignore
@@ -0,0 +1,17 @@
+Debug
+Release
+.vs/
+*.exp
+*.ilk
+*.lib
+*.suo
+*.vcproj.*
+*.vcxproj.*
+*.ncb
+*.suo
+*.dll
+*.pdb
+.deps
+.libs
+*.lo
+*.la
diff --git a/src/hidapi/windows/CMakeLists.txt b/src/hidapi/windows/CMakeLists.txt
new file mode 100644
index 0000000..c8228bc
--- /dev/null
+++ b/src/hidapi/windows/CMakeLists.txt
@@ -0,0 +1,63 @@
+list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_winapi.h")
+
+set(SOURCES
+ hid.c
+ hidapi_cfgmgr32.h
+ hidapi_descriptor_reconstruct.c
+ hidapi_descriptor_reconstruct.h
+ hidapi_hidclass.h
+ hidapi_hidpi.h
+ hidapi_hidsdi.h
+)
+
+if(BUILD_SHARED_LIBS)
+ list(APPEND SOURCES hidapi.rc)
+endif()
+
+add_library(hidapi_winapi
+ ${HIDAPI_PUBLIC_HEADERS}
+ ${SOURCES}
+)
+target_link_libraries(hidapi_winapi
+ PUBLIC hidapi_include
+)
+
+if(NOT BUILD_SHARED_LIBS)
+ target_compile_definitions(hidapi_winapi
+ # prevent marking functions as __declspec(dllexport) for static library build
+ # #480: this should be refactored for v1.0
+ PUBLIC HID_API_NO_EXPORT_DEFINE
+ )
+endif()
+
+set_target_properties(hidapi_winapi
+ PROPERTIES
+ EXPORT_NAME "winapi"
+ OUTPUT_NAME "hidapi"
+ VERSION ${PROJECT_VERSION}
+ PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}"
+)
+
+# compatibility with find_package()
+add_library(hidapi::winapi ALIAS hidapi_winapi)
+# compatibility with raw library link
+add_library(hidapi ALIAS hidapi_winapi)
+
+if(HIDAPI_INSTALL_TARGETS)
+ install(TARGETS hidapi_winapi EXPORT hidapi
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hidapi"
+ )
+endif()
+
+hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi.pc.in")
+
+if(HIDAPI_WITH_TESTS)
+ add_subdirectory(test)
+endif()
+
+if(DEFINED HIDAPI_BUILD_PP_DATA_DUMP AND HIDAPI_BUILD_PP_DATA_DUMP)
+ add_subdirectory(pp_data_dump)
+endif()
diff --git a/src/hidapi/windows/Makefile.am b/src/hidapi/windows/Makefile.am
index 97e261a..2ea5c0d 100644
--- a/src/hidapi/windows/Makefile.am
+++ b/src/hidapi/windows/Makefile.am
@@ -8,7 +8,6 @@
hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h
EXTRA_DIST = \
- ddk_build \
hidapi.vcproj \
hidtest.vcproj \
Makefile-manual \
diff --git a/src/hidapi/windows/Makefile.mingw b/src/hidapi/windows/Makefile.mingw
index b800004..857e4fb 100644
--- a/src/hidapi/windows/Makefile.mingw
+++ b/src/hidapi/windows/Makefile.mingw
@@ -9,16 +9,14 @@
all: hidtest libhidapi.dll
CC=gcc
-CXX=g++
-COBJS=hid.o
-CPPOBJS=../hidtest/hidtest.o
-OBJS=$(COBJS) $(CPPOBJS)
-CFLAGS=-I../hidapi -g -c
-LIBS= -lsetupapi
-DLL_LDFLAGS = -mwindows -lsetupapi
+COBJS=hid.o ../hidtest/test.o
+OBJS=$(COBJS)
+CFLAGS=-I../hidapi -I. -g -c
+LIBS=
+DLL_LDFLAGS = -mwindows
hidtest: $(OBJS)
- g++ -g $^ $(LIBS) -o hidtest
+ $(CC) -g $^ $(LIBS) -o hidtest
libhidapi.dll: $(OBJS)
$(CC) -g $^ $(DLL_LDFLAGS) -o libhidapi.dll
@@ -26,9 +24,6 @@
$(COBJS): %.o: %.c
$(CC) $(CFLAGS) $< -o $@
-$(CPPOBJS): %.o: %.cpp
- $(CXX) $(CFLAGS) $< -o $@
-
clean:
rm *.o ../hidtest/*.o hidtest.exe
diff --git a/src/hidapi/windows/ddk_build/hidapi.def b/src/hidapi/windows/ddk_build/hidapi.def
deleted file mode 100644
index 05e35af..0000000
--- a/src/hidapi/windows/ddk_build/hidapi.def
+++ /dev/null
@@ -1,17 +0,0 @@
-LIBRARY hidapi
-EXPORTS
- hid_open @1
- hid_write @2
- hid_read @3
- hid_close @4
- hid_get_product_string @5
- hid_get_manufacturer_string @6
- hid_get_serial_number_string @7
- hid_get_indexed_string @8
- hid_error @9
- hid_set_nonblocking @10
- hid_enumerate @11
- hid_open_path @12
- hid_send_feature_report @13
- hid_get_feature_report @14
-
\ No newline at end of file
diff --git a/src/hidapi/windows/ddk_build/makefile b/src/hidapi/windows/ddk_build/makefile
deleted file mode 100644
index 637f712..0000000
--- a/src/hidapi/windows/ddk_build/makefile
+++ /dev/null
@@ -1,49 +0,0 @@
-#############################################################################
-#
-# Copyright (C) Microsoft Corporation 1995, 1996
-# All Rights Reserved.
-#
-# MAKEFILE for HID directory
-#
-#############################################################################
-
-!IFDEF WIN95_BUILD
-
-ROOT=..\..\..\..
-
-VERSIONLIST = debug retail
-IS_32 = TRUE
-IS_SDK = TRUE
-IS_PRIVATE = TRUE
-IS_SDK = TRUE
-IS_DDK = TRUE
-WIN32 = TRUE
-COMMONMKFILE = hidapi.mk
-
-!include $(ROOT)\dev\master.mk
-
-
-!ELSE
-
-#
-# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
-# file to this component. This file merely indirects to the real make file
-# that is shared by all the driver components of the Windows NT DDK
-#
-
-!IF DEFINED(_NT_TARGET_VERSION)
-! IF $(_NT_TARGET_VERSION)>=0x501
-! INCLUDE $(NTMAKEENV)\makefile.def
-! ELSE
-# Only warn once per directory
-! INCLUDE $(NTMAKEENV)\makefile.plt
-! IF "$(BUILD_PASS)"=="PASS1"
-! message BUILDMSG: Warning : The sample "$(MAKEDIR)" is not valid for the current OS target.
-! ENDIF
-! ENDIF
-!ELSE
-! INCLUDE $(NTMAKEENV)\makefile.def
-!ENDIF
-
-!ENDIF
-
diff --git a/src/hidapi/windows/ddk_build/sources b/src/hidapi/windows/ddk_build/sources
deleted file mode 100644
index 7f06a09..0000000
--- a/src/hidapi/windows/ddk_build/sources
+++ /dev/null
@@ -1,23 +0,0 @@
-TARGETNAME=hidapi
-TARGETTYPE=DYNLINK
-UMTYPE=console
-UMENTRY=main
-
-MSC_WARNING_LEVEL=/W3 /WX
-
-TARGETLIBS=$(SDK_LIB_PATH)\hid.lib \
- $(SDK_LIB_PATH)\setupapi.lib \
- $(SDK_LIB_PATH)\kernel32.lib \
- $(SDK_LIB_PATH)\comdlg32.lib
-
-USE_MSVCRT=1
-
-INCLUDES= ..\..\hidapi
-SOURCES= ..\hid.c \
-
-
-TARGET_DESTINATION=retail
-
-MUI=0
-MUI_COMMENT="HID Interface DLL"
-
diff --git a/src/hidapi/windows/hid.c b/src/hidapi/windows/hid.c
index 7a292a4..6999691 100644
--- a/src/hidapi/windows/hid.c
+++ b/src/hidapi/windows/hid.c
@@ -5,9 +5,9 @@
Alan Ott
Signal 11 Software
- 8/22/2009
+ libusb/hidapi Team
- Copyright 2009, All Rights Reserved.
+ Copyright 2022, All Rights Reserved.
At the discretion of the user of this library,
this software may be licensed under the terms of the
@@ -19,98 +19,61 @@
code repository located at:
https://github.com/libusb/hidapi .
********************************************************/
-#include "SDL_internal.h"
+#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
+/* Do not warn about wcsncpy usage.
+ https://docs.microsoft.com/cpp/c-runtime-library/security-features-in-the-crt */
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hidapi_winapi.h"
#include <windows.h>
-#ifndef _WIN32_WINNT_WIN8
-#define _WIN32_WINNT_WIN8 0x0602
-#endif
-
-#if 0 /* can cause redefinition errors on some toolchains */
-#ifdef __MINGW32__
-#include <ntdef.h>
-#include <winbase.h>
-#endif
-
-#ifdef __CYGWIN__
-#include <ntdef.h>
-#define _wcsdup wcsdup
-#endif
-#endif /* */
-
#ifndef _NTDEF_
typedef LONG NTSTATUS;
#endif
-/* The maximum number of characters that can be passed into the
- HidD_Get*String() functions without it failing.*/
-#define MAX_STRING_WCHARS 0xFFF
+#ifdef __MINGW32__
+#include <ntdef.h>
+#include <winbase.h>
+#define WC_ERR_INVALID_CHARS 0x00000080
+#endif
+
+#ifdef __CYGWIN__
+#include <ntdef.h>
+#include <wctype.h>
+#define _wcsdup wcsdup
+#endif
/*#define HIDAPI_USE_DDK*/
-/* The timeout in milliseconds for waiting on WriteFile to
- complete in hid_write. The longest observed time to do a output
- report that we've seen is ~200-250ms so let's double that */
-#define HID_WRITE_TIMEOUT_MILLISECONDS 500
+#include "hidapi_cfgmgr32.h"
+#include "hidapi_hidclass.h"
+#include "hidapi_hidsdi.h"
-/* We will only enumerate devices that match these usages */
-#define USAGE_PAGE_GENERIC_DESKTOP 0x0001
-#define USAGE_JOYSTICK 0x0004
-#define USAGE_GAMEPAD 0x0005
-#define USAGE_MULTIAXISCONTROLLER 0x0008
-#define USB_VENDOR_VALVE 0x28de
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
- #include <setupapi.h>
- #include <winioctl.h>
- #include <devpropdef.h>
- #include "hidapi_cfgmgr32.h"
- #include "hidapi_hidclass.h"
- #include "hidapi_hidsdi.h"
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#include "../hidapi/hidapi.h"
-
-/*#include <stdio.h>*/
-/*#include <stdlib.h>*/
-
-/* SDL C runtime functions */
-
-#define calloc SDL_calloc
-#define free SDL_free
-#define malloc SDL_malloc
-#define memcpy SDL_memcpy
-#define memset SDL_memset
-#define strcmp SDL_strcmp
-#define strlen SDL_strlen
-#define strstr SDL_strstr
-#define strtol SDL_strtol
-#define towupper SDL_toupper
-#define wcscmp SDL_wcscmp
-#define wcslen SDL_wcslen
-#define _wcsdup SDL_wcsdup
-#define wcsstr SDL_wcsstr
-
-
+#ifdef MIN
#undef MIN
+#endif
#define MIN(x,y) ((x) < (y)? (x): (y))
-#ifdef _MSC_VER
- /* Yes, we have some unreferenced formal parameters */
- #pragma warning(disable:4100)
-#endif
+/* MAXIMUM_USB_STRING_LENGTH from usbspec.h is 255 */
+/* BLUETOOTH_DEVICE_NAME_SIZE from bluetoothapis.h is 256 */
+#define MAX_STRING_WCHARS 256
-#ifdef __cplusplus
-extern "C" {
-#endif
-
+static struct hid_api_version api_version = {
+ .major = HID_API_VERSION_MAJOR,
+ .minor = HID_API_VERSION_MINOR,
+ .patch = HID_API_VERSION_PATCH
+};
#ifndef HIDAPI_USE_DDK
/* Since we're not building with the DDK, and the HID header
@@ -131,7 +94,6 @@
static HidD_FreePreparsedData_ HidD_FreePreparsedData;
static HidP_GetCaps_ HidP_GetCaps;
static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers;
-static HidD_SetOutputReport_ HidD_SetOutputReport;
static CM_Locate_DevNodeW_ CM_Locate_DevNodeW = NULL;
static CM_Get_Parent_ CM_Get_Parent = NULL;
@@ -144,7 +106,7 @@
static HMODULE cfgmgr32_lib_handle = NULL;
static BOOLEAN hidapi_initialized = FALSE;
-static void free_library_handles(void)
+static void free_library_handles()
{
if (hid_lib_handle)
FreeLibrary(hid_lib_handle);
@@ -154,11 +116,7 @@
cfgmgr32_lib_handle = NULL;
}
-#ifdef __GNUC__
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wcast-function-type"
-#endif
-static int lookup_functions(void)
+static int lookup_functions()
{
hid_lib_handle = LoadLibraryW(L"hid.dll");
if (hid_lib_handle == NULL) {
@@ -170,6 +128,10 @@
goto err;
}
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wcast-function-type"
+#endif
#define RESOLVE(lib_handle, x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) goto err;
RESOLVE(hid_lib_handle, HidD_GetHidGuid);
@@ -185,7 +147,6 @@
RESOLVE(hid_lib_handle, HidD_FreePreparsedData);
RESOLVE(hid_lib_handle, HidP_GetCaps);
RESOLVE(hid_lib_handle, HidD_SetNumInputBuffers);
- RESOLVE(hid_lib_handle, HidD_SetOutputReport);
RESOLVE(cfgmgr32_lib_handle, CM_Locate_DevNodeW);
RESOLVE(cfgmgr32_lib_handle, CM_Get_Parent);
@@ -195,6 +156,9 @@
RESOLVE(cfgmgr32_lib_handle, CM_Get_Device_Interface_ListW);
#undef RESOLVE
+#if defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
return 0;
@@ -202,9 +166,6 @@
free_library_handles();
return -1;
}
-#ifdef __GNUC__
-# pragma GCC diagnostic pop
-#endif
#endif /* HIDAPI_USE_DDK */
@@ -212,50 +173,41 @@
HANDLE device_handle;
BOOL blocking;
USHORT output_report_length;
+ unsigned char *write_buf;
size_t input_report_length;
- void *last_error_str;
- DWORD last_error_num;
+ USHORT feature_report_length;
+ unsigned char *feature_buf;
+ wchar_t *last_error_str;
BOOL read_pending;
char *read_buf;
OVERLAPPED ol;
OVERLAPPED write_ol;
- BOOL use_hid_write_output_report;
+ struct hid_device_info* device_info;
};
-static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
-{
- OSVERSIONINFOEXW osvi;
- DWORDLONG const dwlConditionMask = VerSetConditionMask(
- VerSetConditionMask(
- VerSetConditionMask(
- 0, VER_MAJORVERSION, VER_GREATER_EQUAL ),
- VER_MINORVERSION, VER_GREATER_EQUAL ),
- VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL );
-
- memset(&osvi, 0, sizeof(osvi));
- osvi.dwOSVersionInfoSize = sizeof( osvi );
- osvi.dwMajorVersion = wMajorVersion;
- osvi.dwMinorVersion = wMinorVersion;
- osvi.wServicePackMajor = wServicePackMajor;
-
- return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
-}
-
-static hid_device *new_hid_device(void)
+static hid_device *new_hid_device()
{
hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
+
+ if (dev == NULL) {
+ return NULL;
+ }
+
dev->device_handle = INVALID_HANDLE_VALUE;
dev->blocking = TRUE;
dev->output_report_length = 0;
+ dev->write_buf = NULL;
dev->input_report_length = 0;
+ dev->feature_report_length = 0;
+ dev->feature_buf = NULL;
dev->last_error_str = NULL;
- dev->last_error_num = 0;
dev->read_pending = FALSE;
dev->read_buf = NULL;
memset(&dev->ol, 0, sizeof(dev->ol));
dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL);
memset(&dev->write_ol, 0, sizeof(dev->write_ol));
- dev->write_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL);
+ dev->write_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL);
+ dev->device_info = NULL;
return dev;
}
@@ -265,54 +217,123 @@
CloseHandle(dev->ol.hEvent);
CloseHandle(dev->write_ol.hEvent);
CloseHandle(dev->device_handle);
- LocalFree(dev->last_error_str);
+ free(dev->last_error_str);
+ dev->last_error_str = NULL;
+ free(dev->write_buf);
+ free(dev->feature_buf);
free(dev->read_buf);
+ hid_free_enumeration(dev->device_info);
free(dev);
}
-static void register_error(hid_device *device, const char *op)
+static void register_winapi_error_to_buffer(wchar_t **error_buffer, const WCHAR *op)
{
- WCHAR *ptr, *msg;
+ free(*error_buffer);
+ *error_buffer = NULL;
- DWORD count = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- GetLastError(),
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPWSTR)&msg, 0/*sz*/,
- NULL);
- if (!count)
+ /* Only clear out error messages if NULL is passed into op */
+ if (!op) {
return;
+ }
+
+ WCHAR system_err_buf[1024];
+ DWORD error_code = GetLastError();
+
+ DWORD system_err_len = FormatMessageW(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error_code,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ system_err_buf, ARRAYSIZE(system_err_buf),
+ NULL);
+
+ DWORD op_len = (DWORD)wcslen(op);
+
+ DWORD op_prefix_len =
+ op_len
+ + 15 /*: (0x00000000) */
+ ;
+ DWORD msg_len =
+ + op_prefix_len
+ + system_err_len
+ ;
+
+ *error_buffer = (WCHAR *)calloc(msg_len + 1, sizeof (WCHAR));
+ WCHAR *msg = *error_buffer;
+
+ if (!msg)
+ return;
+
+ int printf_written = swprintf(msg, msg_len + 1, L"%.*ls: (0x%08X) %.*ls", (int)op_len, op, error_code, (int)system_err_len, system_err_buf);
+
+ if (printf_written < 0)
+ {
+ /* Highly unlikely */
+ msg[0] = L'\0';
+ return;
+ }
/* Get rid of the CR and LF that FormatMessage() sticks at the
end of the message. Thanks Microsoft! */
- ptr = msg;
- while (*ptr) {
- if (*ptr == '\r') {
- *ptr = 0x0000;
- break;
- }
- ptr++;
+ while(msg[msg_len-1] == L'\r' || msg[msg_len-1] == L'\n' || msg[msg_len-1] == L' ')
+ {
+ msg[msg_len-1] = L'\0';
+ msg_len--;
}
-
- /* Store the message off in the Device entry so that
- the hid_error() function can pick it up. */
- LocalFree(device->last_error_str);
- device->last_error_str = msg;
}
-static HANDLE open_device(const char *path, BOOL enumerate, BOOL bExclusive )
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
+/* A bug in GCC/mingw gives:
+ * error: array subscript 0 is outside array bounds of 'wchar_t *[0]' {aka 'short unsigned int *[]'} [-Werror=array-bounds]
+ * | free(*error_buffer);
+ * Which doesn't make sense in this context. */
+
+static void register_string_error_to_buffer(wchar_t **error_buffer, const WCHAR *string_error)
+{
+ free(*error_buffer);
+ *error_buffer = NULL;
+
+ if (string_error) {
+ *error_buffer = _wcsdup(string_error);
+ }
+}
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
+
+static void register_winapi_error(hid_device *dev, const WCHAR *op)
+{
+ register_winapi_error_to_buffer(&dev->last_error_str, op);
+}
+
+static void register_string_error(hid_device *dev, const WCHAR *string_error)
+{
+ register_string_error_to_buffer(&dev->last_error_str, string_error);
+}
+
+static wchar_t *last_global_error_str = NULL;
+
+static void register_global_winapi_error(const WCHAR *op)
+{
+ register_winapi_error_to_buffer(&last_global_error_str, op);
+}
+
+static void register_global_error(const WCHAR *string_error)
+{
+ register_string_error_to_buffer(&last_global_error_str, string_error);
+}
+
+static HANDLE open_device(const wchar_t *path, BOOL open_rw)
{
HANDLE handle;
- // Opening with access 0 causes keyboards to stop responding in some system configurations
- // http://steamcommunity.com/discussions/forum/1/1843493219428923893
- // Thanks to co-wie (Ka-wei Low <kawei@mac.com>) for help narrowing down the problem on his system
- //DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ);
- DWORD desired_access = ( GENERIC_WRITE | GENERIC_READ );
- DWORD share_mode = bExclusive ? 0 : ( FILE_SHARE_READ | FILE_SHARE_WRITE );
+ DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0;
+ DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE;
- handle = CreateFileA(path,
+ handle = CreateFileW(path,
desired_access,
share_mode,
NULL,
@@ -323,11 +344,23 @@
return handle;
}
+HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void)
+{
+ return &api_version;
+}
+
+HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
+{
+ return HID_API_VERSION_STR;
+}
+
int HID_API_EXPORT hid_init(void)
{
+ register_global_error(NULL);
#ifndef HIDAPI_USE_DDK
if (!hidapi_initialized) {
if (lookup_functions() < 0) {
+ register_global_winapi_error(L"resolve DLL functions");
return -1;
}
hidapi_initialized = TRUE;
@@ -342,6 +375,7 @@
free_library_handles();
hidapi_initialized = FALSE;
#endif
+ register_global_error(NULL);
return 0;
}
@@ -387,65 +421,178 @@
return property_value;
}
-static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_node)
+static void hid_internal_towupper(wchar_t* string)
{
- wchar_t *manufacturer_string, *serial_number, *product_string;
- /* Manufacturer String */
- manufacturer_string = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_Manufacturer, DEVPROP_TYPE_STRING);
- if (manufacturer_string) {
- free(dev->manufacturer_string);
- dev->manufacturer_string = manufacturer_string;
- }
-
- /* Serial Number String (MAC Address) */
- serial_number = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_DeviceAddress, DEVPROP_TYPE_STRING);
- if (serial_number) {
- free(dev->serial_number);
- dev->serial_number = serial_number;
- }
-
- /* Get devnode grandparent to reach out Bluetooth LE device node */
- if (CM_Get_Parent(&dev_node, dev_node, 0) != CR_SUCCESS)
- return;
-
- /* Product String */
- product_string = hid_internal_get_devnode_property(dev_node, &DEVPKEY_NAME, DEVPROP_TYPE_STRING);
- if (product_string) {
- free(dev->product_string);
- dev->product_string = product_string;
- }
+ for (wchar_t* p = string; *p; ++p) *p = towupper(*p);
}
-#if 0
-/* USB Device Interface Number.
- It can be parsed out of the Hardware ID if a USB device is has multiple interfaces (composite device).
- See https://docs.microsoft.com/windows-hardware/drivers/hid/hidclass-hardware-ids-for-top-level-collections
- and https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers
-
- hardware_id is always expected to be uppercase.
-*/
-static int hid_internal_get_interface_number(const wchar_t* hardware_id)
+static int hid_internal_extract_int_token_value(wchar_t* string, const wchar_t* token)
{
- int interface_number;
- wchar_t *startptr, *endptr;
- const wchar_t *interface_token = L"&MI_";
+ int token_value;
+ wchar_t* startptr, * endptr;
- startptr = wcsstr(hardware_id, interface_token);
+ startptr = wcsstr(string, token);
if (!startptr)
return -1;
- startptr += wcslen(interface_token);
- interface_number = wcstol(startptr, &endptr, 16);
+ startptr += wcslen(token);
+ token_value = wcstol(startptr, &endptr, 16);
if (endptr == startptr)
return -1;
- return interface_number;
+ return token_value;
+}
+
+static void hid_internal_get_usb_info(struct hid_device_info* dev, DEVINST dev_node)
+{
+ wchar_t *device_id = NULL, *hardware_ids = NULL;
+
+ device_id = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);
+ if (!device_id)
+ goto end;
+
+ /* Normalize to upper case */
+ hid_internal_towupper(device_id);
+
+ /* Check for Xbox Common Controller class (XUSB) device.
+ https://docs.microsoft.com/windows/win32/xinput/directinput-and-xusb-devices
+ https://docs.microsoft.com/windows/win32/xinput/xinput-and-directinput
+ */
+ if (hid_internal_extract_int_token_value(device_id, L"IG_") != -1) {
+ /* Get devnode parent to reach out USB device. */
+ if (CM_Get_Parent(&dev_node, dev_node, 0) != CR_SUCCESS)
+ goto end;
+ }
+
+ /* Get the hardware ids from devnode */
+ hardware_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_HardwareIds, DEVPROP_TYPE_STRING_LIST);
+ if (!hardware_ids)
+ goto end;
+
+ /* Get additional information from USB device's Hardware ID
+ https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers
+ https://docs.microsoft.com/windows-hardware/drivers/usbcon/enumeration-of-interfaces-not-grouped-in-collections
+ */
+ for (wchar_t* hardware_id = hardware_ids; *hardware_id; hardware_id += wcslen(hardware_id) + 1) {
+ /* Normalize to upper case */
+ hid_internal_towupper(hardware_id);
+
+ if (dev->release_number == 0) {
+ /* USB_DEVICE_DESCRIPTOR.bcdDevice value. */
+ int release_number = hid_internal_extract_int_token_value(hardware_id, L"REV_");
+ if (release_number != -1) {
+ dev->release_number = (unsigned short)release_number;
+ }
+ }
+
+ if (dev->interface_number == -1) {
+ /* USB_INTERFACE_DESCRIPTOR.bInterfaceNumber value. */
+ int interface_number = hid_internal_extract_int_token_value(hardware_id, L"MI_");
+ if (interface_number != -1) {
+ dev->interface_number = interface_number;
+ }
+ }
+ }
+
+ /* Try to get USB device manufacturer string if not provided by HidD_GetManufacturerString. */
+ if (wcslen(dev->manufacturer_string) == 0) {
+ wchar_t* manufacturer_string = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_Manufacturer, DEVPROP_TYPE_STRING);
+ if (manufacturer_string) {
+ free(dev->manufacturer_string);
+ dev->manufacturer_string = manufacturer_string;
+ }
+ }
+
+ /* Try to get USB device serial number if not provided by HidD_GetSerialNumberString. */
+ if (wcslen(dev->serial_number) == 0) {
+ DEVINST usb_dev_node = dev_node;
+ if (dev->interface_number != -1) {
+ /* Get devnode parent to reach out composite parent USB device.
+ https://docs.microsoft.com/windows-hardware/drivers/usbcon/enumeration-of-the-composite-parent-device
+ */
+ if (CM_Get_Parent(&usb_dev_node, dev_node, 0) != CR_SUCCESS)
+ goto end;
+ }
+
+ /* Get the device id of the USB device. */
+ free(device_id);
+ device_id = hid_internal_get_devnode_property(usb_dev_node, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);
+ if (!device_id)
+ goto end;
+
+ /* Extract substring after last '\\' of Instance ID.
+ For USB devices it may contain device's serial number.
+ https://docs.microsoft.com/windows-hardware/drivers/install/instance-ids
+ */
+ for (wchar_t *ptr = device_id + wcslen(device_id); ptr > device_id; --ptr) {
+ /* Instance ID is unique only within the scope of the bus.
+ For USB devices it means that serial number is not available. Skip. */
+ if (*ptr == L'&')
+ break;
+
+ if (*ptr == L'\\') {
+ free(dev->serial_number);
+ dev->serial_number = _wcsdup(ptr + 1);
+ break;
+ }
+ }
+ }
+
+ /* If we can't get the interface number, it means that there is only one interface. */
+ if (dev->interface_number == -1)
+ dev->interface_number = 0;
+
+end:
+ free(device_id);
+ free(hardware_ids);
+}
+
+/* HidD_GetProductString/HidD_GetManufacturerString/HidD_GetSerialNumberString is not working for BLE HID devices
+ Request this info via dev node properties instead.
+ https://docs.microsoft.com/answers/questions/401236/hidd-getproductstring-with-ble-hid-device.html
+*/
+static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_node)
+{
+ if (wcslen(dev->manufacturer_string) == 0) {
+ /* Manufacturer Name String (UUID: 0x2A29) */
+ wchar_t* manufacturer_string = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_Manufacturer, DEVPROP_TYPE_STRING);
+ if (manufacturer_string) {
+ free(dev->manufacturer_string);
+ dev->manufacturer_string = manufacturer_string;
+ }
+ }
+
+ if (wcslen(dev->serial_number) == 0) {
+ /* Serial Number String (UUID: 0x2A25) */
+ wchar_t* serial_number = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_DeviceAddress, DEVPROP_TYPE_STRING);
+ if (serial_number) {
+ free(dev->serial_number);
+ dev->serial_number = serial_number;
+ }
+ }
+
+ if (wcslen(dev->product_string) == 0) {
+ /* Model Number String (UUID: 0x2A24) */
+ wchar_t* product_string = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_ModelNumber, DEVPROP_TYPE_STRING);
+ if (!product_string) {
+ DEVINST parent_dev_node = 0;
+ /* Fallback: Get devnode grandparent to reach out Bluetooth LE device node */
+ if (CM_Get_Parent(&parent_dev_node, dev_node, 0) == CR_SUCCESS) {
+ /* Device Name (UUID: 0x2A00) */
+ product_string = hid_internal_get_devnode_property(parent_dev_node, &DEVPKEY_NAME, DEVPROP_TYPE_STRING);
+ }
+ }
+
+ if (product_string) {
+ free(dev->product_string);
+ dev->product_string = product_string;
+ }
+ }
}
static void hid_internal_get_info(const wchar_t* interface_path, struct hid_device_info* dev)
{
- wchar_t *device_id = NULL, *compatible_ids = NULL, *hardware_ids = NULL;
- wchar_t *id;
+ wchar_t *device_id = NULL, *compatible_ids = NULL;
CONFIGRET cr;
DEVINST dev_node;
@@ -459,23 +606,6 @@
if (cr != CR_SUCCESS)
goto end;
- /* Get the hardware ids from devnode */
- hardware_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_HardwareIds, DEVPROP_TYPE_STRING_LIST);
- if (!hardware_ids)
- goto end;
-
- /* Search for interface number in hardware ids */
- for (id = hardware_ids; *id; id += wcslen(id) + 1) {
- /* Normalize to upper case */
- wchar_t* p = id;
- for (; *p; ++p) *p = towupper(*p);
-
- dev->interface_number = hid_internal_get_interface_number(id);
-
- if (dev->interface_number != -1)
- break;
- }
-
/* Get devnode parent */
cr = CM_Get_Parent(&dev_node, dev_node, 0);
if (cr != CR_SUCCESS)
@@ -487,54 +617,49 @@
goto end;
/* Now we can parse parent's compatible IDs to find out the device bus type */
- for (id = compatible_ids; *id; id += wcslen(id) + 1) {
+ for (wchar_t* compatible_id = compatible_ids; *compatible_id; compatible_id += wcslen(compatible_id) + 1) {
/* Normalize to upper case */
- wchar_t* p = id;
- for (; *p; ++p) *p = towupper(*p);
+ hid_internal_towupper(compatible_id);
/* USB devices
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support
https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */
- if (wcsstr(id, L"USB") != NULL) {
+ if (wcsstr(compatible_id, L"USB") != NULL) {
dev->bus_type = HID_API_BUS_USB;
+ hid_internal_get_usb_info(dev, dev_node);
break;
}
/* Bluetooth devices
https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */
- if (wcsstr(id, L"BTHENUM") != NULL) {
+ if (wcsstr(compatible_id, L"BTHENUM") != NULL) {
dev->bus_type = HID_API_BUS_BLUETOOTH;
break;
}
/* Bluetooth LE devices */
- if (wcsstr(id, L"BTHLEDEVICE") != NULL) {
- /* HidD_GetProductString/HidD_GetManufacturerString/HidD_GetSerialNumberString is not working for BLE HID devices
- Request this info via dev node properties instead.
- https://docs.microsoft.com/answers/questions/401236/hidd-getproductstring-with-ble-hid-device.html */
- hid_internal_get_ble_info(dev, dev_node);
-
+ if (wcsstr(compatible_id, L"BTHLEDEVICE") != NULL) {
dev->bus_type = HID_API_BUS_BLUETOOTH;
+ hid_internal_get_ble_info(dev, dev_node);
break;
}
/* I2C devices
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */
- if (wcsstr(id, L"PNP0C50") != NULL) {
+ if (wcsstr(compatible_id, L"PNP0C50") != NULL) {
dev->bus_type = HID_API_BUS_I2C;
break;
}
/* SPI devices
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */
- if (wcsstr(id, L"PNP0C51") != NULL) {
+ if (wcsstr(compatible_id, L"PNP0C51") != NULL) {
dev->bus_type = HID_API_BUS_SPI;
break;
}
}
end:
free(device_id);
- free(hardware_ids);
free(compatible_ids);
}
@@ -552,7 +677,6 @@
return dst;
}
-#endif /* 0 */
static wchar_t *hid_internal_UTF8toUTF16(const char *src)
{
@@ -569,299 +693,148 @@
return dst;
}
-static int hid_get_bluetooth_info(const char *path, struct hid_device_info* dev)
+static struct hid_device_info *hid_internal_get_device_info(const wchar_t *path, HANDLE handle)
{
- wchar_t *interface_path = NULL, *device_id = NULL, *compatible_ids = NULL;
- wchar_t *id;
- CONFIGRET cr;
- DEVINST dev_node;
- int is_bluetooth = 0;
+ struct hid_device_info *dev = NULL; /* return object */
+ HIDD_ATTRIBUTES attrib;
+ PHIDP_PREPARSED_DATA pp_data = NULL;
+ HIDP_CAPS caps;
+ wchar_t string[MAX_STRING_WCHARS];
- /* Get the device id from interface path */
- interface_path = hid_internal_UTF8toUTF16(path);
- device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);
- if (!device_id)
- goto end;
+ /* Create the record. */
+ dev = (struct hid_device_info*)calloc(1, sizeof(struct hid_device_info));
- /* Open devnode from device id */
- cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL);
- if (cr != CR_SUCCESS)
- goto end;
-
- /* Get devnode parent */
- cr = CM_Get_Parent(&dev_node, dev_node, 0);
- if (cr != CR_SUCCESS)
- goto end;
-
- /* Get the compatible ids from parent devnode */
- compatible_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_CompatibleIds, DEVPROP_TYPE_STRING_LIST);
- if (!compatible_ids)
- goto end;
-
- /* Now we can parse parent's compatible IDs to find out the device bus type */
- for (id = compatible_ids; *id; id += wcslen(id) + 1) {
- /* Normalize to upper case */
- wchar_t* p = id;
- for (; *p; ++p) *p = (wchar_t)towupper(*p);
-
- /* USB devices
- https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support
- https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */
- if (wcsstr(id, L"USB") != NULL) {
- /*dev->bus_type = HID_API_BUS_USB;*/
- break;
- }
-
- /* Bluetooth devices
- https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */
- if (wcsstr(id, L"BTHENUM") != NULL) {
- /*dev->bus_type = HID_API_BUS_BLUETOOTH;*/
- is_bluetooth = 1;
- break;
- }
-
- /* Bluetooth LE devices */
- if (wcsstr(id, L"BTHLEDEVICE") != NULL) {
- /* HidD_GetProductString/HidD_GetManufacturerString/HidD_GetSerialNumberString is not working for BLE HID devices
- Request this info via dev node properties instead.
- https://docs.microsoft.com/answers/questions/401236/hidd-getproductstring-with-ble-hid-device.html */
- if (dev)
- hid_internal_get_ble_info(dev, dev_node);
-
- /*dev->bus_type = HID_API_BUS_BLUETOOTH;*/
- is_bluetooth = 1;
- break;
- }
-
- /* I2C devices
- https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */
- if (wcsstr(id, L"PNP0C50") != NULL) {
- /*dev->bus_type = HID_API_BUS_I2C;*/
- break;
- }
-
- /* SPI devices
- https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */
- if (wcsstr(id, L"PNP0C51") != NULL) {
- /*dev->bus_type = HID_API_BUS_SPI;*/
- break;
- }
+ if (dev == NULL) {
+ return NULL;
}
-end:
- free(interface_path);
- free(device_id);
- free(compatible_ids);
- return is_bluetooth;
-}
-static int hid_blacklist(unsigned short vendor_id, unsigned short product_id)
-{
- size_t i;
- static const struct { unsigned short vid; unsigned short pid; } known_bad[] = {
- /* Causes deadlock when asking for device details... */
- { 0x1B1C, 0x1B3D }, /* Corsair Gaming keyboard */
- { 0x1532, 0x0109 }, /* Razer Lycosa Gaming keyboard */
- { 0x1532, 0x010B }, /* Razer Arctosa Gaming keyboard */
- { 0x045E, 0x0822 }, /* Microsoft Precision Mouse */
- { 0x0D8C, 0x0014 }, /* Sharkoon Skiller SGH2 headset */
- { 0x1CCF, 0x0000 }, /* All Konami Amusement Devices */
+ /* Fill out the record */
+ dev->next = NULL;
+ dev->path = hid_internal_UTF16toUTF8(path);
+ dev->interface_number = -1;
- /* Turns into an Android controller when enumerated... */
- { 0x0738, 0x2217 } /* SPEEDLINK COMPETITION PRO */
- };
+ attrib.Size = sizeof(HIDD_ATTRIBUTES);
+ if (HidD_GetAttributes(handle, &attrib)) {
+ /* VID/PID */
+ dev->vendor_id = attrib.VendorID;
+ dev->product_id = attrib.ProductID;
- for (i = 0; i < (sizeof(known_bad)/sizeof(known_bad[0])); i++) {
- if ((vendor_id == known_bad[i].vid) && (product_id == known_bad[i].pid || known_bad[i].pid == 0x0000)) {
- return 1;
- }
- }
+ /* Release Number */
+ dev->release_number = attrib.VersionNumber;
+ }
- return 0;
+ /* Get the Usage Page and Usage for this device. */
+ if (HidD_GetPreparsedData(handle, &pp_data)) {
+ if (HidP_GetCaps(pp_data, &caps) == HIDP_STATUS_SUCCESS) {
+ dev->usage_page = caps.UsagePage;
+ dev->usage = caps.Usage;
+ }
+
+ HidD_FreePreparsedData(pp_data);
+ }
+
+ /* Serial Number */
+ string[0] = L'\0';
+ HidD_GetSerialNumberString(handle, string, sizeof(string));
+ string[MAX_STRING_WCHARS - 1] = L'\0';
+ dev->serial_number = _wcsdup(string);
+
+ /* Manufacturer String */
+ string[0] = L'\0';
+ HidD_GetManufacturerString(handle, string, sizeof(string));
+ string[MAX_STRING_WCHARS - 1] = L'\0';
+ dev->manufacturer_string = _wcsdup(string);
+
+ /* Product String */
+ string[0] = L'\0';
+ HidD_GetProductString(handle, string, sizeof(string));
+ string[MAX_STRING_WCHARS - 1] = L'\0';
+ dev->product_string = _wcsdup(string);
+
+ hid_internal_get_info(path, dev);
+
+ return dev;
}
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
{
- BOOL res;
struct hid_device_info *root = NULL; /* return object */
struct hid_device_info *cur_dev = NULL;
+ GUID interface_class_guid;
+ CONFIGRET cr;
+ wchar_t* device_interface_list = NULL;
+ DWORD len;
- /* Windows objects for interacting with the driver. */
- GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };
- SP_DEVINFO_DATA devinfo_data;
- SP_DEVICE_INTERFACE_DATA device_interface_data;
- SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL;
- HDEVINFO device_info_set = INVALID_HANDLE_VALUE;
- int device_index = 0;
- const char *hint = SDL_GetHint(SDL_HINT_HIDAPI_IGNORE_DEVICES);
-
- if (hid_init() < 0)
+ if (hid_init() < 0) {
+ /* register_global_error: global error is reset by hid_init */
return NULL;
+ }
- /* Initialize the Windows objects. */
- memset(&devinfo_data, 0x0, sizeof(devinfo_data));
- devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
- device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ /* Retrieve HID Interface Class GUID
+ https://docs.microsoft.com/windows-hardware/drivers/install/guid-devinterface-hid */
+ HidD_GetHidGuid(&interface_class_guid);
- /* Get information for all the devices belonging to the HID class. */
- device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
-
- /* Iterate over each device in the HID class, looking for the right one. */
-
- for (;;) {
- HANDLE write_handle = INVALID_HANDLE_VALUE;
- DWORD required_size = 0;
- HIDD_ATTRIBUTES attrib;
-
- res = SetupDiEnumDeviceInterfaces(device_info_set,
- NULL,
- &InterfaceClassGuid,
- device_index,
- &device_interface_data);
-
- if (!res) {
- /* A return of FALSE from this function means that
- there are no more devices. */
+ /* Get the list of all device interfaces belonging to the HID class. */
+ /* Retry in case of list was changed between calls to
+ CM_Get_Device_Interface_List_SizeW and CM_Get_Device_Interface_ListW */
+ do {
+ cr = CM_Get_Device_Interface_List_SizeW(&len, &interface_class_guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
+ if (cr != CR_SUCCESS) {
+ register_global_error(L"Failed to get size of HID device interface list");
break;
}
- /* Call with 0-sized detail size, and let the function
- tell us how long the detail struct needs to be. The
- size is put in &required_size. */
- res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
- &device_interface_data,
- NULL,
- 0,
- &required_size,
- NULL);
-
- /* Allocate a long enough structure for device_interface_detail_data. */
- device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size);
- device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
-
- /* Get the detailed data for this device. The detail data gives us
- the device path for this device, which is then passed into
- CreateFile() to get a handle to the device. */
- res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
- &device_interface_data,
- device_interface_detail_data,
- required_size,
- NULL,
- NULL);
-
- if (!res) {
- /* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail");
- Continue to the next device. */
- goto cont;
+ if (device_interface_list != NULL) {
+ free(device_interface_list);
}
- /* XInput devices don't get real HID reports and are better handled by the raw input driver */
- if (strstr(device_interface_detail_data->DevicePath, "&ig_") != NULL) {
- goto cont;
+ device_interface_list = (wchar_t*)calloc(len, sizeof(wchar_t));
+ if (device_interface_list == NULL) {
+ register_global_error(L"Failed to allocate memory for HID device interface list");
+ return NULL;
}
-
- /* Make sure this device is of Setup Class "HIDClass" and has a
- driver bound to it. */
- /* In the main HIDAPI tree this is a loop which will erroneously open
- devices that aren't HID class. Please preserve this delta if we ever
- update to take new changes */
- {
- char driver_name[256];
-
- /* Populate devinfo_data. This function will return failure
- when there are no more interfaces left. */
- res = SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data);
-
- if (!res)
- goto cont;
-
- res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
- SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
- if (!res)
- goto cont;
-
- if (strcmp(driver_name, "HIDClass") == 0) {
- /* See if there's a driver bound. */
- res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
- SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
- if (!res)
- goto cont;
- }
- else
- {
- goto cont;
- }
+ cr = CM_Get_Device_Interface_ListW(&interface_class_guid, NULL, device_interface_list, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
+ if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) {
+ register_global_error(L"Failed to get HID device interface list");
}
+ } while (cr == CR_BUFFER_SMALL);
- //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);
+ if (cr != CR_SUCCESS) {
+ goto end_of_function;
+ }
- /* Open a handle to the device */
- write_handle = open_device(device_interface_detail_data->DevicePath, TRUE, FALSE);
+ /* Iterate over each device interface in the HID class, looking for the right one. */
+ for (wchar_t* device_interface = device_interface_list; *device_interface; device_interface += wcslen(device_interface) + 1) {
+ HANDLE device_handle = INVALID_HANDLE_VALUE;
+ HIDD_ATTRIBUTES attrib;
- /* Check validity of write_handle. */
- if (write_handle == INVALID_HANDLE_VALUE) {
+ /* Open read-only handle to the device */
+ device_handle = open_device(device_interface, FALSE);
+
+ /* Check validity of device_handle. */
+ if (device_handle == INVALID_HANDLE_VALUE) {
/* Unable to open the device. */
- //register_error(dev, "CreateFile");
- goto cont;
+ continue;
}
-
/* Get the Vendor ID and Product ID for this device. */
attrib.Size = sizeof(HIDD_ATTRIBUTES);
- HidD_GetAttributes(write_handle, &attrib);
- //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID);
-
- /* See if there are any devices we should skip in enumeration */
- if (hint) {
- char vendor_match[16], product_match[16];
- SDL_snprintf(vendor_match, sizeof(vendor_match), "0x%.4x/0x0000", attrib.VendorID);
- SDL_snprintf(product_match, sizeof(product_match), "0x%.4x/0x%.4x", attrib.VendorID, attrib.ProductID);
- if (SDL_strcasestr(hint, vendor_match) || SDL_strcasestr(hint, product_match)) {
- continue;
- }
+ if (!HidD_GetAttributes(device_handle, &attrib)) {
+ goto cont_close;
}
/* Check the VID/PID to see if we should add this
device to the enumeration list. */
if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) &&
- (product_id == 0x0 || attrib.ProductID == product_id) &&
- !hid_blacklist(attrib.VendorID, attrib.ProductID)) {
+ (product_id == 0x0 || attrib.ProductID == product_id)) {
- #define WSTR_LEN 512
- const char *str;
- struct hid_device_info *tmp;
- PHIDP_PREPARSED_DATA pp_data = NULL;
- HIDP_CAPS caps;
- BOOLEAN hidp_res;
- NTSTATUS nt_res;
- wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */
- size_t len;
+ /* VID/PID match. Create the record. */
+ struct hid_device_info *tmp = hid_internal_get_device_info(device_interface, device_handle);
- /* Get the Usage Page and Usage for this device. */
- hidp_res = HidD_GetPreparsedData(write_handle, &pp_data);
- if (hidp_res) {
- nt_res = HidP_GetCaps(pp_data, &caps);
- HidD_FreePreparsedData(pp_data);
- if (nt_res != HIDP_STATUS_SUCCESS) {
- goto cont_close;
- }
- }
- else {
+ if (tmp == NULL) {
goto cont_close;
}
- /* SDL Modification: Ignore the device if it's not a gamepad. This limits compatibility
- risk from devices that may respond poorly to our string queries below. */
- if (attrib.VendorID != USB_VENDOR_VALVE) {
- if (caps.UsagePage != USAGE_PAGE_GENERIC_DESKTOP) {
- goto cont_close;
- }
- if (caps.Usage != USAGE_JOYSTICK && caps.Usage != USAGE_GAMEPAD && caps.Usage != USAGE_MULTIAXISCONTROLLER) {
- goto cont_close;
- }
- }
-
- /* VID/PID match. Create the record. */
- tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
if (cur_dev) {
cur_dev->next = tmp;
}
@@ -869,86 +842,24 @@
root = tmp;
}
cur_dev = tmp;
-
- /* Fill out the record */
- cur_dev->usage_page = caps.UsagePage;
- cur_dev->usage = caps.Usage;
- cur_dev->next = NULL;
- str = device_interface_detail_data->DevicePath;
- if (str) {
- len = strlen(str);
- cur_dev->path = (char*) calloc(len+1, sizeof(char));
- memcpy(cur_dev->path, str, len+1);
- }
- else
- cur_dev->path = NULL;
-
- /* Serial Number */
- hidp_res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr));
- wstr[WSTR_LEN-1] = 0x0000;
- if (hidp_res) {
- cur_dev->serial_number = _wcsdup(wstr);
- }
-
- /* Manufacturer String */
- hidp_res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr));
- wstr[WSTR_LEN-1] = 0x0000;
- if (hidp_res) {
- cur_dev->manufacturer_string = _wcsdup(wstr);
- }
-
- /* Product String */
- hidp_res = HidD_GetProductString(write_handle, wstr, sizeof(wstr));
- wstr[WSTR_LEN-1] = 0x0000;
- if (hidp_res) {
- cur_dev->product_string = _wcsdup(wstr);
- }
-
- /* VID/PID */
- cur_dev->vendor_id = attrib.VendorID;
- cur_dev->product_id = attrib.ProductID;
-
- /* Release Number */
- cur_dev->release_number = attrib.VersionNumber;
-
- /* Interface Number. It can sometimes be parsed out of the path
- on Windows if a device has multiple interfaces. See
- http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or
- search for "Hardware IDs for HID Devices" at MSDN. If it's not
- in the path, it's set to -1. */
- cur_dev->interface_number = -1;
- if (cur_dev->path) {
- char *interface_component = strstr(cur_dev->path, "&mi_");
- if (interface_component) {
- char *hex_str = interface_component + 4;
- char *endptr = NULL;
- cur_dev->interface_number = strtol(hex_str, &endptr, 16);
- if (endptr == hex_str) {
- /* The parsing failed. Set interface_number to -1. */
- cur_dev->interface_number = -1;
- }
- }
- }
-
- /* Get the Bluetooth device info */
- hid_get_bluetooth_info(cur_dev->path, cur_dev);
}
cont_close:
- CloseHandle(write_handle);
-cont:
- /* We no longer need the detail data. It can be freed */
- free(device_interface_detail_data);
-
- device_index++;
-
+ CloseHandle(device_handle);
}
- /* Close the device information handle. */
- SetupDiDestroyDeviceInfoList(device_info_set);
+ if (root == NULL) {
+ if (vendor_id == 0 && product_id == 0) {
+ register_global_error(L"No HID devices found in the system.");
+ } else {
+ register_global_error(L"No HID devices with requested VID/PID found in the system.");
+ }
+ }
+
+end_of_function:
+ free(device_interface_list);
return root;
-
}
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
@@ -966,7 +877,6 @@
}
}
-
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
{
/* TODO: Merge this functions with the Linux version. This function should be platform independent. */
@@ -974,13 +884,19 @@
const char *path_to_open = NULL;
hid_device *handle = NULL;
+ /* register_global_error: global error is reset by hid_enumerate/hid_init */
devs = hid_enumerate(vendor_id, product_id);
+ if (!devs) {
+ /* register_global_error: global error is already set by hid_enumerate */
+ return NULL;
+ }
+
cur_dev = devs;
while (cur_dev) {
if (cur_dev->vendor_id == vendor_id &&
cur_dev->product_id == product_id) {
if (serial_number) {
- if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
+ if (cur_dev->serial_number && wcscmp(serial_number, cur_dev->serial_number) == 0) {
path_to_open = cur_dev->path;
break;
}
@@ -995,7 +911,9 @@
if (path_to_open) {
/* Open the device */
- handle = hid_open_path(path_to_open, 0);
+ handle = hid_open_path(path_to_open);
+ } else {
+ register_global_error(L"Device with requested VID/PID/(SerialNumber) not found");
}
hid_free_enumeration(devs);
@@ -1003,151 +921,173 @@
return handle;
}
-HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bExclusive)
+HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
{
- hid_device *dev;
- HIDP_CAPS caps;
+ hid_device *dev = NULL;
+ wchar_t* interface_path = NULL;
+ HANDLE device_handle = INVALID_HANDLE_VALUE;
PHIDP_PREPARSED_DATA pp_data = NULL;
- BOOLEAN res;
- NTSTATUS nt_res;
+ HIDP_CAPS caps;
if (hid_init() < 0) {
- return NULL;
+ /* register_global_error: global error is reset by hid_init */
+ goto end_of_function;
+ }
+
+ interface_path = hid_internal_UTF8toUTF16(path);
+ if (!interface_path) {
+ register_global_error(L"Path conversion failure");
+ goto end_of_function;
+ }
+
+ /* Open a handle to the device */
+ device_handle = open_device(interface_path, TRUE);
+
+ /* Check validity of write_handle. */
+ if (device_handle == INVALID_HANDLE_VALUE) {
+ /* System devices, such as keyboards and mice, cannot be opened in
+ read-write mode, because the system takes exclusive control over
+ them. This is to prevent keyloggers. However, feature reports
+ can still be sent and received. Retry opening the device, but
+ without read/write access. */
+ device_handle = open_device(interface_path, FALSE);
+
+ /* Check the validity of the limited device_handle. */
+ if (device_handle == INVALID_HANDLE_VALUE) {
+ register_global_winapi_error(L"open_device");
+ goto end_of_function;
+ }
+ }
+
+ /* Set the Input Report buffer size to 64 reports. */
+ if (!HidD_SetNumInputBuffers(device_handle, 64)) {
+ register_global_winapi_error(L"set input buffers");
+ goto end_of_function;
+ }
+
+ /* Get the Input Report length for the device. */
+ if (!HidD_GetPreparsedData(device_handle, &pp_data)) {
+ register_global_winapi_error(L"get preparsed data");
+ goto end_of_function;
+ }
+
+ if (HidP_GetCaps(pp_data, &caps) != HIDP_STATUS_SUCCESS) {
+ register_global_error(L"HidP_GetCaps");
+ goto end_of_function;
}
dev = new_hid_device();
- /* Open a handle to the device */
- dev->device_handle = open_device(path, FALSE, bExclusive);
-
- /* Check validity of write_handle. */
- if (dev->device_handle == INVALID_HANDLE_VALUE) {
- /* Unable to open the device. */
- register_error(dev, "CreateFile");
- goto err;
+ if (dev == NULL) {
+ register_global_error(L"hid_device allocation error");
+ goto end_of_function;
}
- /* Set the Input Report buffer size to 64 reports. */
- res = HidD_SetNumInputBuffers(dev->device_handle, 64);
- if (!res) {
- register_error(dev, "HidD_SetNumInputBuffers");
- goto err;
- }
+ dev->device_handle = device_handle;
+ device_handle = INVALID_HANDLE_VALUE;
- /* Get the Input Report length for the device. */
- res = HidD_GetPreparsedData(dev->device_handle, &pp_data);
- if (!res) {
- register_error(dev, "HidD_GetPreparsedData");
- goto err;
- }
- nt_res = HidP_GetCaps(pp_data, &caps);
- if (nt_res != HIDP_STATUS_SUCCESS) {
- register_error(dev, "HidP_GetCaps");
- goto err_pp_data;
- }
dev->output_report_length = caps.OutputReportByteLength;
dev->input_report_length = caps.InputReportByteLength;
- HidD_FreePreparsedData(pp_data);
-
- /* On Windows 7, we need to use hid_write_output_report() over Bluetooth */
- if (dev->output_report_length > 512) {
- dev->use_hid_write_output_report = !IsWindowsVersionOrGreater( HIBYTE( _WIN32_WINNT_WIN8 ), LOBYTE( _WIN32_WINNT_WIN8 ), 0 );
- }
-
+ dev->feature_report_length = caps.FeatureReportByteLength;
dev->read_buf = (char*) malloc(dev->input_report_length);
+ dev->device_info = hid_internal_get_device_info(interface_path, dev->device_handle);
+
+end_of_function:
+ free(interface_path);
+ CloseHandle(device_handle);
+
+ if (pp_data) {
+ HidD_FreePreparsedData(pp_data);
+ }
return dev;
-
-err_pp_data:
- HidD_FreePreparsedData(pp_data);
-err:
- free_hid_device(dev);
- return NULL;
}
-int HID_API_EXPORT HID_API_CALL hid_write_output_report(hid_device *dev, const unsigned char *data, size_t length)
+int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
{
+ DWORD bytes_written = 0;
+ int function_result = -1;
BOOL res;
- res = HidD_SetOutputReport(dev->device_handle, (void *)data, (ULONG)length);
- if (res)
- return (int)length;
- else
- return -1;
-}
+ BOOL overlapped = FALSE;
-static int hid_write_timeout(hid_device *dev, const unsigned char *data, size_t length, int milliseconds)
-{
- DWORD bytes_written;
- BOOL res;
unsigned char *buf;
- if (dev->use_hid_write_output_report) {
- return hid_write_output_report(dev, data, length);
+ if (!data || !length) {
+ register_string_error(dev, L"Zero buffer/length");
+ return function_result;
}
+ register_string_error(dev, NULL);
+
/* Make sure the right number of bytes are passed to WriteFile. Windows
expects the number of bytes which are in the _longest_ report (plus
one for the report number) bytes even if the data is a report
which is shorter than that. Windows gives us this value in
caps.OutputReportByteLength. If a user passes in fewer bytes than this,
- create a temporary buffer which is the proper size. */
+ use cached temporary buffer which is the proper size. */
if (length >= dev->output_report_length) {
/* The user passed the right number of bytes. Use the buffer as-is. */
buf = (unsigned char *) data;
} else {
- /* Create a temporary buffer and copy the user's data
- into it, padding the rest with zeros. */
- buf = (unsigned char *) malloc(dev->output_report_length);
+ if (dev->write_buf == NULL)
+ dev->write_buf = (unsigned char *) malloc(dev->output_report_length);
+ buf = dev->write_buf;
memcpy(buf, data, length);
memset(buf + length, 0, dev->output_report_length - length);
length = dev->output_report_length;
}
- res = WriteFile( dev->device_handle, buf, ( DWORD ) length, NULL, &dev->write_ol );
+ res = WriteFile(dev->device_handle, buf, (DWORD) length, NULL, &dev->write_ol);
+
if (!res) {
if (GetLastError() != ERROR_IO_PENDING) {
/* WriteFile() failed. Return error. */
- register_error(dev, "WriteFile");
- bytes_written = (DWORD) -1;
+ register_winapi_error(dev, L"WriteFile");
+ goto end_of_function;
+ }
+ overlapped = TRUE;
+ }
+
+ if (overlapped) {
+ /* Wait for the transaction to complete. This makes
+ hid_write() synchronous. */
+ res = WaitForSingleObject(dev->write_ol.hEvent, 1000);
+ if (res != WAIT_OBJECT_0) {
+ /* There was a Timeout. */
+ register_winapi_error(dev, L"hid_write/WaitForSingleObject");
+ goto end_of_function;
+ }
+
+ /* Get the result. */
+ res = GetOverlappedResult(dev->device_handle, &dev->write_ol, &bytes_written, FALSE/*wait*/);
+ if (res) {
+ function_result = bytes_written;
+ }
+ else {
+ /* The Write operation failed. */
+ register_winapi_error(dev, L"hid_write/GetOverlappedResult");
goto end_of_function;
}
}
- /* Wait here until the write is done. This makes hid_write() synchronous. */
- res = WaitForSingleObject(dev->write_ol.hEvent, milliseconds);
- if (res != WAIT_OBJECT_0)
- {
- // There was a Timeout.
- bytes_written = (DWORD) -1;
- register_error(dev, "WriteFile/WaitForSingleObject Timeout");
- goto end_of_function;
- }
-
- res = GetOverlappedResult(dev->device_handle, &dev->write_ol, &bytes_written, FALSE/*F=don't_wait*/);
- if (!res) {
- /* The Write operation failed. */
- register_error(dev, "WriteFile");
- bytes_written = (DWORD) -1;
- goto end_of_function;
- }
-
end_of_function:
- if (buf != data)
- free(buf);
-
- return bytes_written;
+ return function_result;
}
-int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
-{
- return hid_write_timeout(dev, data, length, HID_WRITE_TIMEOUT_MILLISECONDS);
-}
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
{
DWORD bytes_read = 0;
size_t copy_len = 0;
- BOOL res;
+ BOOL res = FALSE;
+ BOOL overlapped = FALSE;
+
+ if (!data || !length) {
+ register_string_error(dev, L"Zero buffer/length");
+ return -1;
+ }
+
+ register_string_error(dev, NULL);
/* Copy the handle for convenience. */
HANDLE ev = dev->ol.hEvent;
@@ -1157,33 +1097,40 @@
dev->read_pending = TRUE;
memset(dev->read_buf, 0, dev->input_report_length);
ResetEvent(ev);
- res = ReadFile(dev->device_handle, dev->read_buf, (DWORD)dev->input_report_length, &bytes_read, &dev->ol);
+ res = ReadFile(dev->device_handle, dev->read_buf, (DWORD) dev->input_report_length, &bytes_read, &dev->ol);
if (!res) {
if (GetLastError() != ERROR_IO_PENDING) {
/* ReadFile() has failed.
Clean up and return error. */
+ register_winapi_error(dev, L"ReadFile");
CancelIo(dev->device_handle);
dev->read_pending = FALSE;
goto end_of_function;
}
+ overlapped = TRUE;
}
}
-
- /* See if there is any data yet. */
- res = WaitForSingleObject(ev, milliseconds >= 0 ? milliseconds : INFINITE);
- if (res != WAIT_OBJECT_0) {
- /* There was no data this time. Return zero bytes available,
- but leave the Overlapped I/O running. */
- return 0;
+ else {
+ overlapped = TRUE;
}
- /* Get the number of bytes read. The actual data has been copied to the data[]
- array which was passed to ReadFile(). We must not wait here because we've
- already waited on our event above, and since it's auto-reset, it will have
- been reset back to unsignalled by now. */
- res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, FALSE/*don't wait*/);
+ if (overlapped) {
+ if (milliseconds >= 0) {
+ /* See if there is any data yet. */
+ res = WaitForSingleObject(ev, milliseconds);
+ if (res != WAIT_OBJECT_0) {
+ /* There was no data this time. Return zero bytes available,
+ but leave the Overlapped I/O running. */
+ return 0;
+ }
+ }
+ /* Either WaitForSingleObject() told us that ReadFile has completed, or
+ we are in non-blocking mode. Get the number of bytes read. The actual
+ data has been copied to the data[] array which was passed to ReadFile(). */
+ res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
+ }
/* Set pending back to false, even if GetOverlappedResult() returned error. */
dev->read_pending = FALSE;
@@ -1203,14 +1150,16 @@
memcpy(data, dev->read_buf, copy_len);
}
}
+ if (!res) {
+ register_winapi_error(dev, L"hid_read_timeout/GetOverlappedResult");
+ }
end_of_function:
if (!res) {
- register_error(dev, "GetOverlappedResult");
return -1;
}
- return (int)copy_len;
+ return (int) copy_len;
}
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
@@ -1226,42 +1175,69 @@
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
{
- BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, (ULONG)length);
- if (!res) {
- register_error(dev, "HidD_SetFeature");
+ BOOL res = FALSE;
+ unsigned char *buf;
+ size_t length_to_send;
+
+ if (!data || !length) {
+ register_string_error(dev, L"Zero buffer/length");
return -1;
}
- return (int)length;
+ register_string_error(dev, NULL);
+
+ /* Windows expects at least caps.FeatureReportByteLength bytes passed
+ to HidD_SetFeature(), even if the report is shorter. Any less sent and
+ the function fails with error ERROR_INVALID_PARAMETER set. Any more
+ and HidD_SetFeature() silently truncates the data sent in the report
+ to caps.FeatureReportByteLength. */
+ if (length >= dev->feature_report_length) {
+ buf = (unsigned char *) data;
+ length_to_send = length;
+ } else {
+ if (dev->feature_buf == NULL)
+ dev->feature_buf = (unsigned char *) malloc(dev->feature_report_length);
+ buf = dev->feature_buf;
+ memcpy(buf, data, length);
+ memset(buf + length, 0, dev->feature_report_length - length);
+ length_to_send = dev->feature_report_length;
+ }
+
+ res = HidD_SetFeature(dev->device_handle, (PVOID)buf, (DWORD) length_to_send);
+
+ if (!res) {
+ register_winapi_error(dev, L"HidD_SetFeature");
+ return -1;
+ }
+
+ return (int) length;
}
-
-int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
+static int hid_get_report(hid_device *dev, DWORD report_type, unsigned char *data, size_t length)
{
BOOL res;
-#if 0
- res = HidD_GetFeature(dev->device_handle, (PVOID)data, (ULONG)length);
- if (!res) {
- register_error(dev, "HidD_GetFeature");
- return -1;
- }
- return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */
-#else
- DWORD bytes_returned;
+ DWORD bytes_returned = 0;
OVERLAPPED ol;
memset(&ol, 0, sizeof(ol));
+ if (!data || !length) {
+ register_string_error(dev, L"Zero buffer/length");
+ return -1;
+ }
+
+ register_string_error(dev, NULL);
+
res = DeviceIoControl(dev->device_handle,
- IOCTL_HID_GET_FEATURE,
- data, (DWORD)length,
- data, (DWORD)length,
+ report_type,
+ data, (DWORD) length,
+ data, (DWORD) length,
&bytes_returned, &ol);
if (!res) {
if (GetLastError() != ERROR_IO_PENDING) {
/* DeviceIoControl() failed. Return error. */
- register_error(dev, "Send Feature Report DeviceIoControl");
+ register_winapi_error(dev, L"Get Input/Feature Report DeviceIoControl");
return -1;
}
}
@@ -1271,157 +1247,209 @@
res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/);
if (!res) {
/* The operation failed. */
- register_error(dev, "Send Feature Report GetOverLappedResult");
+ register_winapi_error(dev, L"Get Input/Feature Report GetOverLappedResult");
return -1;
}
+ /* When numbered reports aren't used,
+ bytes_returned seem to include only what is actually received from the device
+ (not including the first byte with 0, as an indication "no numbered reports"). */
+ if (data[0] == 0x0) {
+ bytes_returned++;
+ }
+
return bytes_returned;
-#endif
+}
+
+int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
+{
+ /* We could use HidD_GetFeature() instead, but it doesn't give us an actual length, unfortunately */
+ return hid_get_report(dev, IOCTL_HID_GET_FEATURE, data, length);
+}
+
+int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
+{
+ /* We could use HidD_GetInputReport() instead, but it doesn't give us an actual length, unfortunately */
+ return hid_get_report(dev, IOCTL_HID_GET_INPUT_REPORT, data, length);
}
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
{
- typedef BOOL (WINAPI *CancelIoEx_t)(HANDLE hFile, LPOVERLAPPED lpOverlapped);
- CancelIoEx_t CancelIoExFunc = (CancelIoEx_t)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "CancelIoEx");
-
if (!dev)
return;
- if (CancelIoExFunc) {
- CancelIoExFunc(dev->device_handle, NULL);
- } else {
- /* Windows XP, this will only cancel I/O on the current thread */
- CancelIo(dev->device_handle);
- }
- if (dev->read_pending) {
- DWORD bytes_read = 0;
-
- GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
- }
+ CancelIo(dev->device_handle);
free_hid_device(dev);
}
int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
- BOOL res;
-
- res = HidD_GetManufacturerString(dev->device_handle, string, (ULONG)(sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)));
- if (!res) {
- register_error(dev, "HidD_GetManufacturerString");
+ if (!string || !maxlen) {
+ register_string_error(dev, L"Zero buffer/length");
return -1;
}
+ if (!dev->device_info) {
+ register_string_error(dev, L"NULL device info");
+ return -1;
+ }
+
+ wcsncpy(string, dev->device_info->manufacturer_string, maxlen);
+ string[maxlen - 1] = L'\0';
+
+ register_string_error(dev, NULL);
+
return 0;
}
int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
- BOOL res;
-
- res = HidD_GetProductString(dev->device_handle, string, (ULONG)(sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)));
- if (!res) {
- register_error(dev, "HidD_GetProductString");
+ if (!string || !maxlen) {
+ register_string_error(dev, L"Zero buffer/length");
return -1;
}
+ if (!dev->device_info) {
+ register_string_error(dev, L"NULL device info");
+ return -1;
+ }
+
+ wcsncpy(string, dev->device_info->product_string, maxlen);
+ string[maxlen - 1] = L'\0';
+
+ register_string_error(dev, NULL);
+
return 0;
}
int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
- BOOL res;
-
- res = HidD_GetSerialNumberString(dev->device_handle, string, (ULONG)(sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)));
- if (!res) {
- register_error(dev, "HidD_GetSerialNumberString");
+ if (!string || !maxlen) {
+ register_string_error(dev, L"Zero buffer/length");
return -1;
}
+ if (!dev->device_info) {
+ register_string_error(dev, L"NULL device info");
+ return -1;
+ }
+
+ wcsncpy(string, dev->device_info->serial_number, maxlen);
+ string[maxlen - 1] = L'\0';
+
+ register_string_error(dev, NULL);
+
return 0;
}
+HID_API_EXPORT struct hid_device_info * HID_API_CALL hid_get_device_info(hid_device *dev) {
+ if (!dev->device_info)
+ {
+ register_string_error(dev, L"NULL device info");
+ return NULL;
+ }
+
+ return dev->device_info;
+}
+
int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
{
BOOL res;
- res = HidD_GetIndexedString(dev->device_handle, string_index, string, (ULONG)(sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)));
+ res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS));
if (!res) {
- register_error(dev, "HidD_GetIndexedString");
+ register_winapi_error(dev, L"HidD_GetIndexedString");
return -1;
}
+ register_string_error(dev, NULL);
+
return 0;
}
+int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *container_id)
+{
+ wchar_t *interface_path = NULL, *device_id = NULL;
+ CONFIGRET cr = CR_FAILURE;
+ DEVINST dev_node;
+ DEVPROPTYPE property_type;
+ ULONG len;
+
+ if (!container_id) {
+ register_string_error(dev, L"Invalid Container ID");
+ return -1;
+ }
+
+ register_string_error(dev, NULL);
+
+ interface_path = hid_internal_UTF8toUTF16(dev->device_info->path);
+ if (!interface_path) {
+ register_string_error(dev, L"Path conversion failure");
+ goto end;
+ }
+
+ /* Get the device id from interface path */
+ device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);
+ if (!device_id) {
+ register_string_error(dev, L"Failed to get device interface property InstanceId");
+ goto end;
+ }
+
+ /* Open devnode from device id */
+ cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL);
+ if (cr != CR_SUCCESS) {
+ register_string_error(dev, L"Failed to locate device node");
+ goto end;
+ }
+
+ /* Get the container id from devnode */
+ len = sizeof(*container_id);
+ cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_ContainerId, &property_type, (PBYTE)container_id, &len, 0);
+ if (cr == CR_SUCCESS && property_type != DEVPROP_TYPE_GUID)
+ cr = CR_FAILURE;
+
+ if (cr != CR_SUCCESS)
+ register_string_error(dev, L"Failed to read ContainerId property from device node");
+
+end:
+ free(interface_path);
+ free(device_id);
+
+ return cr == CR_SUCCESS ? 0 : -1;
+}
+
+
+int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
+{
+ PHIDP_PREPARSED_DATA pp_data = NULL;
+
+ if (!HidD_GetPreparsedData(dev->device_handle, &pp_data) || pp_data == NULL) {
+ register_string_error(dev, L"HidD_GetPreparsedData");
+ return -1;
+ }
+
+ int res = hid_winapi_descriptor_reconstruct_pp_data(pp_data, buf, buf_size);
+
+ HidD_FreePreparsedData(pp_data);
+
+ return res;
+}
+
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{
- return (wchar_t*)dev->last_error_str;
+ if (dev) {
+ if (dev->last_error_str == NULL)
+ return L"Success";
+ return (wchar_t*)dev->last_error_str;
+ }
+
+ if (last_global_error_str == NULL)
+ return L"Success";
+ return last_global_error_str;
}
-
-#if 0
-
-/*#define PICPGM*/
-/*#define S11*/
-#define P32
-#ifdef S11
- unsigned short VendorID = 0xa0a0;
- unsigned short ProductID = 0x0001;
-#endif
-
-#ifdef P32
- unsigned short VendorID = 0x04d8;
- unsigned short ProductID = 0x3f;
-#endif
-
-#ifdef PICPGM
- unsigned short VendorID = 0x04d8;
- unsigned short ProductID = 0x0033;
-#endif
-
-int __cdecl main(int argc, char* argv[])
-{
- int i, res;
- unsigned char buf[65];
-
- UNREFERENCED_PARAMETER(argc);
- UNREFERENCED_PARAMETER(argv);
-
- /* Set up the command buffer. */
- memset(buf,0x00,sizeof(buf));
- buf[0] = 0;
- buf[1] = 0x81;
-
-
- /* Open the device. */
- int handle = open(VendorID, ProductID, L"12345");
- if (handle < 0)
- printf("unable to open device\n");
-
-
- /* Toggle LED (cmd 0x80) */
- buf[1] = 0x80;
- res = write(handle, buf, 65);
- if (res < 0)
- printf("Unable to write()\n");
-
- /* Request state (cmd 0x81) */
- buf[1] = 0x81;
- write(handle, buf, 65);
- if (res < 0)
- printf("Unable to write() (2)\n");
-
- /* Read requested state */
- read(handle, buf, 65);
- if (res < 0)
- printf("Unable to read()\n");
-
- /* Print out the returned buffer. */
- for (i = 0; i < 4; i++)
- printf("buf[%d]: %d\n", i, buf[i]);
-
- return 0;
-}
+#ifndef hidapi_winapi_EXPORTS
+#include "hidapi_descriptor_reconstruct.c"
#endif
#ifdef __cplusplus
diff --git a/src/hidapi/windows/hidapi.rc b/src/hidapi/windows/hidapi.rc
new file mode 100644
index 0000000..530917e
--- /dev/null
+++ b/src/hidapi/windows/hidapi.rc
@@ -0,0 +1,35 @@
+#include "winresrc.h"
+
+#include "hidapi.h"
+
+// English
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION HID_API_VERSION_MAJOR,HID_API_VERSION_MINOR,HID_API_VERSION_PATCH,0
+ PRODUCTVERSION HID_API_VERSION_MAJOR,HID_API_VERSION_MINOR,HID_API_VERSION_PATCH,0
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS 0
+#ifdef _DEBUG
+ | VS_FF_DEBUG
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "04090000"
+ BEGIN
+ VALUE "CompanyName", "libusb/hidapi Team"
+ VALUE "FileDescription", "A multi-platform library to interface with HID devices (USB, Bluetooth, etc.)"
+ VALUE "FileVersion", HID_API_VERSION_STR
+ VALUE "ProductName", "HIDAPI"
+ VALUE "ProductVersion", HID_API_VERSION_STR
+ VALUE "Licence", "https://github.com/libusb/hidapi/blob/master/LICENSE.txt"
+ VALUE "Comments", "https://github.com/libusb/hidapi"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 0
+ END
+END
diff --git a/src/hidapi/windows/hidapi.sln b/src/hidapi/windows/hidapi.sln
index af4076c..aeb2660 100644
--- a/src/hidapi/windows/hidapi.sln
+++ b/src/hidapi/windows/hidapi.sln
@@ -1,29 +1,41 @@
-
-Microsoft Visual Studio Solution File, Format Version 10.00
-# Visual C++ Express 2008
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidapi", "hidapi.vcproj", "{A107C21C-418A-4697-BB10-20C3AA60E2E4}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidtest", "hidtest.vcproj", "{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}"
- ProjectSection(ProjectDependencies) = postProject
- {A107C21C-418A-4697-BB10-20C3AA60E2E4} = {A107C21C-418A-4697-BB10-20C3AA60E2E4}
- EndProjectSection
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Win32 = Debug|Win32
- Release|Win32 = Release|Win32
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|Win32.ActiveCfg = Debug|Win32
- {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|Win32.Build.0 = Debug|Win32
- {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.ActiveCfg = Release|Win32
- {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.Build.0 = Release|Win32
- {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|Win32.ActiveCfg = Debug|Win32
- {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|Win32.Build.0 = Debug|Win32
- {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.ActiveCfg = Release|Win32
- {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.Build.0 = Release|Win32
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.136
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidapi", "hidapi.vcxproj", "{A107C21C-418A-4697-BB10-20C3AA60E2E4}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidtest", "hidtest.vcxproj", "{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|Win32.ActiveCfg = Debug|Win32
+ {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|Win32.Build.0 = Debug|Win32
+ {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|x64.ActiveCfg = Debug|x64
+ {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|x64.Build.0 = Debug|x64
+ {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.ActiveCfg = Release|Win32
+ {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.Build.0 = Release|Win32
+ {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|x64.ActiveCfg = Release|x64
+ {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|x64.Build.0 = Release|x64
+ {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|Win32.ActiveCfg = Debug|Win32
+ {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|Win32.Build.0 = Debug|Win32
+ {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|x64.ActiveCfg = Debug|x64
+ {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|x64.Build.0 = Debug|x64
+ {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.ActiveCfg = Release|Win32
+ {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.Build.0 = Release|Win32
+ {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|x64.ActiveCfg = Release|x64
+ {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {8749E535-9C65-4A89-840E-78D7578C7866}
+ EndGlobalSection
+EndGlobal
diff --git a/src/hidapi/windows/hidapi.vcproj b/src/hidapi/windows/hidapi.vcproj
index aea5a0b..e331064 100644
--- a/src/hidapi/windows/hidapi.vcproj
+++ b/src/hidapi/windows/hidapi.vcproj
@@ -1,201 +1,200 @@
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="9.00"
- Name="hidapi"
- ProjectGUID="{A107C21C-418A-4697-BB10-20C3AA60E2E4}"
- RootNamespace="hidapi"
- Keyword="Win32Proj"
- TargetFrameworkVersion="196613"
- >
- <Platforms>
- <Platform
- Name="Win32"
- />
- </Platforms>
- <ToolFiles>
- </ToolFiles>
- <Configurations>
- <Configuration
- Name="Debug|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
- ConfigurationType="2"
- CharacterSet="1"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- AdditionalIncludeDirectories="..\hidapi"
- PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS"
- MinimalRebuild="true"
- BasicRuntimeChecks="3"
- RuntimeLibrary="3"
- UsePrecompiledHeader="0"
- WarningLevel="3"
- DebugInformationFormat="4"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="setupapi.lib"
- LinkIncremental="2"
- GenerateDebugInformation="true"
- SubSystem="2"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- />
- </Configuration>
- <Configuration
- Name="Release|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
- ConfigurationType="2"
- CharacterSet="1"
- WholeProgramOptimization="1"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- EnableIntrinsicFunctions="true"
- AdditionalIncludeDirectories="..\hidapi"
- PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS"
- RuntimeLibrary="2"
- EnableFunctionLevelLinking="true"
- UsePrecompiledHeader="0"
- WarningLevel="3"
- DebugInformationFormat="3"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="setupapi.lib"
- LinkIncremental="1"
- GenerateDebugInformation="true"
- SubSystem="2"
- OptimizeReferences="2"
- EnableCOMDATFolding="2"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- />
- </Configuration>
- </Configurations>
- <References>
- </References>
- <Files>
- <Filter
- Name="Source Files"
- Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
- UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
- >
- <File
- RelativePath=".\hid.c"
- >
- </File>
- </Filter>
- <Filter
- Name="Header Files"
- Filter="h;hpp;hxx;hm;inl;inc;xsd"
- UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
- >
- <File
- RelativePath="..\hidapi\hidapi.h"
- >
- </File>
- </Filter>
- <Filter
- Name="Resource Files"
- Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
- UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
- >
- </Filter>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="hidapi"
+ ProjectGUID="{A107C21C-418A-4697-BB10-20C3AA60E2E4}"
+ RootNamespace="hidapi"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\hidapi"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="..\hidapi"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="setupapi.lib"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\hid.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath="..\hidapi\hidapi.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src/hidapi/windows/hidapi.vcxproj b/src/hidapi/windows/hidapi.vcxproj
new file mode 100644
index 0000000..7f7a95d
--- /dev/null
+++ b/src/hidapi/windows/hidapi.vcxproj
@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{A107C21C-418A-4697-BB10-20C3AA60E2E4}</ProjectGuid>
+ <RootNamespace>hidapi</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='11'">v110</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='12'">v120</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='14'">v140</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='15'">v141</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='16'">v142</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='17'">v143</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='11'">v110</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='12'">v120</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='14'">v140</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='15'">v141</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='16'">v142</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='17'">v143</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='11'">v110</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='12'">v120</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='14'">v140</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='15'">v141</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='16'">v142</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='17'">v143</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='11'">v110</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='12'">v120</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='14'">v140</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='15'">v141</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='16'">v142</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='17'">v143</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>14.0.25431.1</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>..\hidapi</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>..\hidapi</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>..\hidapi</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>NDEBUG</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>..\hidapi</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>NDEBUG</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="hid.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\hidapi\hidapi.h" />
+ <ClInclude Include="hidapi_winapi.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="hidapi.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/src/hidapi/windows/hidapi_cfgmgr32.h b/src/hidapi/windows/hidapi_cfgmgr32.h
index 071c5b4..638512a 100644
--- a/src/hidapi/windows/hidapi_cfgmgr32.h
+++ b/src/hidapi/windows/hidapi_cfgmgr32.h
@@ -28,18 +28,14 @@
#include <propkey.h>
#else
-#ifndef PROPERTYKEY_DEFINED
-#define PROPERTYKEY_DEFINED
-typedef struct
-{
- GUID fmtid;
- DWORD pid;
-} PROPERTYKEY;
-#endif /* PROPERTYKEY_DEFINED */
/* This part of the header mimics cfgmgr32.h,
but only what is used by HIDAPI */
+#include <initguid.h>
+#include <devpropdef.h>
+#include <propkeydef.h>
+
typedef DWORD RETURN_TYPE;
typedef RETURN_TYPE CONFIGRET;
typedef DWORD DEVNODE, DEVINST;
@@ -59,18 +55,20 @@
typedef CONFIGRET(__stdcall* CM_Get_DevNode_PropertyW_)(DEVINST dnDevInst, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags);
typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_PropertyW_)(LPCWSTR pszDeviceInterface, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags);
typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_List_SizeW_)(PULONG pulLen, LPGUID InterfaceClassGuid, DEVINSTID_W pDeviceID, ULONG ulFlags);
-typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_ListW_)(LPGUID InterfaceClassGuid, DEVINSTID_W pDeviceID, WCHAR* Buffer, ULONG BufferLen, ULONG ulFlags);
+typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_ListW_)(LPGUID InterfaceClassGuid, DEVINSTID_W pDeviceID, PZZWSTR Buffer, ULONG BufferLen, ULONG ulFlags);
// from devpkey.h
-static DEVPROPKEY DEVPKEY_NAME = { { 0xb725f130, 0x47ef, 0x101a, {0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac} }, 10 }; // DEVPROP_TYPE_STRING
-static DEVPROPKEY DEVPKEY_Device_InstanceId = { { 0x78c34fc8, 0x104a, 0x4aca, {0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57} }, 256 }; // DEVPROP_TYPE_STRING
-//static DEVPROPKEY DEVPKEY_Device_HardwareIds = { { 0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0} }, 3 }; // DEVPROP_TYPE_STRING_LIST
-static DEVPROPKEY DEVPKEY_Device_CompatibleIds = { { 0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0} }, 4 }; // DEVPROP_TYPE_STRING_LIST
-//static DEVPROPKEY DEVPKEY_Device_ContainerId = { { 0x8c7ed206, 0x3f8a, 0x4827, {0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c} }, 2 }; // DEVPROP_TYPE_GUID
+DEFINE_DEVPROPKEY(DEVPKEY_NAME, 0xb725f130, 0x47ef, 0x101a, 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac, 10); // DEVPROP_TYPE_STRING
+DEFINE_DEVPROPKEY(DEVPKEY_Device_Manufacturer, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 13); // DEVPROP_TYPE_STRING
+DEFINE_DEVPROPKEY(DEVPKEY_Device_InstanceId, 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 256); // DEVPROP_TYPE_STRING
+DEFINE_DEVPROPKEY(DEVPKEY_Device_HardwareIds, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 3); // DEVPROP_TYPE_STRING_LIST
+DEFINE_DEVPROPKEY(DEVPKEY_Device_CompatibleIds, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 4); // DEVPROP_TYPE_STRING_LIST
+DEFINE_DEVPROPKEY(DEVPKEY_Device_ContainerId, 0x8c7ed206, 0x3f8a, 0x4827, 0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c, 2); // DEVPROP_TYPE_GUID
// from propkey.h
-static PROPERTYKEY PKEY_DeviceInterface_Bluetooth_DeviceAddress = { { 0x2bd67d8b, 0x8beb, 0x48d5, {0x87, 0xe0, 0x6c, 0xda, 0x34, 0x28, 0x04, 0x0a} }, 1 }; // DEVPROP_TYPE_STRING
-static PROPERTYKEY PKEY_DeviceInterface_Bluetooth_Manufacturer = { { 0x2bd67d8b, 0x8beb, 0x48d5, {0x87, 0xe0, 0x6c, 0xda, 0x34, 0x28, 0x04, 0x0a} }, 4 }; // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceInterface_Bluetooth_DeviceAddress, 0x2BD67D8B, 0x8BEB, 0x48D5, 0x87, 0xE0, 0x6C, 0xDA, 0x34, 0x28, 0x04, 0x0A, 1); // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceInterface_Bluetooth_Manufacturer, 0x2BD67D8B, 0x8BEB, 0x48D5, 0x87, 0xE0, 0x6C, 0xDA, 0x34, 0x28, 0x04, 0x0A, 4); // DEVPROP_TYPE_STRING
+DEFINE_PROPERTYKEY(PKEY_DeviceInterface_Bluetooth_ModelNumber, 0x2BD67D8B, 0x8BEB, 0x48D5, 0x87, 0xE0, 0x6C, 0xDA, 0x34, 0x28, 0x04, 0x0A, 5); // DEVPROP_TYPE_STRING
#endif
diff --git a/src/hidapi/windows/hidapi_descriptor_reconstruct.c b/src/hidapi/windows/hidapi_descriptor_reconstruct.c
new file mode 100644
index 0000000..a4efb25
--- /dev/null
+++ b/src/hidapi/windows/hidapi_descriptor_reconstruct.c
@@ -0,0 +1,987 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ libusb/hidapi Team
+
+ Copyright 2022, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+ https://github.com/libusb/hidapi .
+********************************************************/
+#include "hidapi_descriptor_reconstruct.h"
+
+/**
+ * @brief References to report descriptor buffer.
+ *
+ */
+struct rd_buffer {
+ unsigned char* buf; /* Pointer to the array which stores the reconstructed descriptor */
+ size_t buf_size; /* Size of the buffer in bytes */
+ size_t byte_idx; /* Index of the next report byte to write to buf array */
+};
+
+/**
+ * @brief Function that appends a byte to encoded report descriptor buffer.
+ *
+ * @param[in] byte Single byte to append.
+ * @param rpt_desc Pointer to report descriptor buffer struct.
+ */
+static void rd_append_byte(unsigned char byte, struct rd_buffer* rpt_desc) {
+ if (rpt_desc->byte_idx < rpt_desc->buf_size) {
+ rpt_desc->buf[rpt_desc->byte_idx] = byte;
+ rpt_desc->byte_idx++;
+ }
+}
+
+/**
+ * @brief Writes a short report descriptor item according USB HID spec 1.11 chapter 6.2.2.2.
+ *
+ * @param[in] rd_item Enumeration identifying type (Main, Global, Local) and function (e.g Usage or Report Count) of the item.
+ * @param[in] data Data (Size depends on rd_item 0,1,2 or 4bytes).
+ * @param list Chained list of report descriptor bytes.
+ *
+ * @return Returns 0 if successful, -1 for error.
+ */
+static int rd_write_short_item(rd_items rd_item, LONG64 data, struct rd_buffer* rpt_desc) {
+ if (rd_item & 0x03) {
+ // Invalid input data, last to bits are reserved for data size
+ return -1;
+ }
+
+ if (rd_item == rd_main_collection_end) {
+ // Item without data (1Byte prefix only)
+ unsigned char oneBytePrefix = (unsigned char) rd_item + 0x00;
+ rd_append_byte(oneBytePrefix, rpt_desc);
+ }
+ else if ((rd_item == rd_global_logical_minimum) ||
+ (rd_item == rd_global_logical_maximum) ||
+ (rd_item == rd_global_physical_minimum) ||
+ (rd_item == rd_global_physical_maximum)) {
+ // Item with signed integer data
+ if ((data >= -128) && (data <= 127)) {
+ // 1Byte prefix + 1Byte data
+ unsigned char oneBytePrefix = (unsigned char) rd_item + 0x01;
+ char localData = (char)data;
+ rd_append_byte(oneBytePrefix, rpt_desc);
+ rd_append_byte(localData & 0xFF, rpt_desc);
+ }
+ else if ((data >= -32768) && (data <= 32767)) {
+ // 1Byte prefix + 2Byte data
+ unsigned char oneBytePrefix = (unsigned char) rd_item + 0x02;
+ INT16 localData = (INT16)data;
+ rd_append_byte(oneBytePrefix, rpt_desc);
+ rd_append_byte(localData & 0xFF, rpt_desc);
+ rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
+ }
+ else if ((data >= -2147483648LL) && (data <= 2147483647)) {
+ // 1Byte prefix + 4Byte data
+ unsigned char oneBytePrefix = (unsigned char) rd_item + 0x03;
+ INT32 localData = (INT32)data;
+ rd_append_byte(oneBytePrefix, rpt_desc);
+ rd_append_byte(localData & 0xFF, rpt_desc);
+ rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
+ rd_append_byte(localData >> 16 & 0xFF, rpt_desc);
+ rd_append_byte(localData >> 24 & 0xFF, rpt_desc);
+ }
+ else {
+ // Data out of 32 bit signed integer range
+ return -1;
+ }
+ }
+ else {
+ // Item with unsigned integer data
+ if ((data >= 0) && (data <= 0xFF)) {
+ // 1Byte prefix + 1Byte data
+ unsigned char oneBytePrefix = (unsigned char) rd_item + 0x01;
+ unsigned char localData = (unsigned char)data;
+ rd_append_byte(oneBytePrefix, rpt_desc);
+ rd_append_byte(localData & 0xFF, rpt_desc);
+ }
+ else if ((data >= 0) && (data <= 0xFFFF)) {
+ // 1Byte prefix + 2Byte data
+ unsigned char oneBytePrefix = (unsigned char) rd_item + 0x02;
+ UINT16 localData = (UINT16)data;
+ rd_append_byte(oneBytePrefix, rpt_desc);
+ rd_append_byte(localData & 0xFF, rpt_desc);
+ rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
+ }
+ else if ((data >= 0) && (data <= 0xFFFFFFFF)) {
+ // 1Byte prefix + 4Byte data
+ unsigned char oneBytePrefix = (unsigned char) rd_item + 0x03;
+ UINT32 localData = (UINT32)data;
+ rd_append_byte(oneBytePrefix, rpt_desc);
+ rd_append_byte(localData & 0xFF, rpt_desc);
+ rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
+ rd_append_byte(localData >> 16 & 0xFF, rpt_desc);
+ rd_append_byte(localData >> 24 & 0xFF, rpt_desc);
+ }
+ else {
+ // Data out of 32 bit unsigned integer range
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static struct rd_main_item_node * rd_append_main_item_node(int first_bit, int last_bit, rd_node_type type_of_node, int caps_index, int collection_index, rd_main_items main_item_type, unsigned char report_id, struct rd_main_item_node **list) {
+ struct rd_main_item_node *new_list_node;
+
+ // Determine last node in the list
+ while (*list != NULL)
+ {
+ list = &(*list)->next;
+ }
+
+ new_list_node = malloc(sizeof(*new_list_node)); // Create new list entry
+ new_list_node->FirstBit = first_bit;
+ new_list_node->LastBit = last_bit;
+ new_list_node->TypeOfNode = type_of_node;
+ new_list_node->CapsIndex = caps_index;
+ new_list_node->CollectionIndex = collection_index;
+ new_list_node->MainItemType = main_item_type;
+ new_list_node->ReportID = report_id;
+ new_list_node->next = NULL; // NULL marks last node in the list
+
+ *list = new_list_node;
+ return new_list_node;
+}
+
+static struct rd_main_item_node * rd_insert_main_item_node(int first_bit, int last_bit, rd_node_type type_of_node, int caps_index, int collection_index, rd_main_items main_item_type, unsigned char report_id, struct rd_main_item_node **list) {
+ // Insert item after the main item node referenced by list
+ struct rd_main_item_node *next_item = (*list)->next;
+ (*list)->next = NULL;
+ rd_append_main_item_node(first_bit, last_bit, type_of_node, caps_index, collection_index, main_item_type, report_id, list);
+ (*list)->next->next = next_item;
+ return (*list)->next;
+}
+
+static struct rd_main_item_node * rd_search_main_item_list_for_bit_position(int search_bit, rd_main_items main_item_type, unsigned char report_id, struct rd_main_item_node **list) {
+ // Determine first INPUT/OUTPUT/FEATURE main item, where the last bit position is equal or greater than the search bit position
+
+ while (((*list)->next->MainItemType != rd_collection) &&
+ ((*list)->next->MainItemType != rd_collection_end) &&
+ !(((*list)->next->LastBit >= search_bit) &&
+ ((*list)->next->ReportID == report_id) &&
+ ((*list)->next->MainItemType == main_item_type))
+ )
+ {
+ list = &(*list)->next;
+ }
+ return *list;
+}
+
+int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned char *buf, size_t buf_size)
+{
+ hidp_preparsed_data *pp_data = (hidp_preparsed_data *) preparsed_data;
+
+ // Check if MagicKey is correct, to ensure that pp_data points to an valid preparse data structure
+ if (memcmp(pp_data->MagicKey, "HidP KDR", 8) != 0) {
+ return -1;
+ }
+
+ struct rd_buffer rpt_desc = {
+ .buf = buf,
+ .buf_size = buf_size,
+ .byte_idx = 0
+ };
+
+ // Set pointer to the first node of link_collection_nodes
+ phid_pp_link_collection_node link_collection_nodes = (phid_pp_link_collection_node)(((unsigned char*)&pp_data->caps[0]) + pp_data->FirstByteOfLinkCollectionArray);
+
+ // ****************************************************************************************************************************
+ // Create lookup tables for the bit range of each report per collection (position of first bit and last bit in each collection)
+ // coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE]
+ // ****************************************************************************************************************************
+
+ // Allocate memory and initialize lookup table
+ rd_bit_range ****coll_bit_range;
+ coll_bit_range = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_bit_range));
+ for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
+ coll_bit_range[collection_node_idx] = malloc(256 * sizeof(*coll_bit_range[0])); // 256 possible report IDs (incl. 0x00)
+ for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
+ coll_bit_range[collection_node_idx][reportid_idx] = malloc(NUM_OF_HIDP_REPORT_TYPES * sizeof(*coll_bit_range[0][0]));
+ for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+ coll_bit_range[collection_node_idx][reportid_idx][rt_idx] = malloc(sizeof(rd_bit_range));
+ coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = -1;
+ coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = -1;
+ }
+ }
+ }
+
+ // Fill the lookup table where caps exist
+ for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+ for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) {
+ int first_bit, last_bit;
+ first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8
+ + pp_data->caps[caps_idx].BitPosition;
+ last_bit = first_bit + pp_data->caps[caps_idx].ReportSize
+ * pp_data->caps[caps_idx].ReportCount - 1;
+ if (coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit == -1 ||
+ coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit > first_bit) {
+ coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit = first_bit;
+ }
+ if (coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->LastBit < last_bit) {
+ coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->LastBit = last_bit;
+ }
+ }
+ }
+
+ // *************************************************************************
+ // -Determine hierachy levels of each collections and store it in:
+ // coll_levels[COLLECTION_INDEX]
+ // -Determine number of direct childs of each collections and store it in:
+ // coll_number_of_direct_childs[COLLECTION_INDEX]
+ // *************************************************************************
+ int max_coll_level = 0;
+ int *coll_levels = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_levels[0]));
+ int *coll_number_of_direct_childs = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_number_of_direct_childs[0]));
+ for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
+ coll_levels[collection_node_idx] = -1;
+ coll_number_of_direct_childs[collection_node_idx] = 0;
+ }
+
+ {
+ int actual_coll_level = 0;
+ USHORT collection_node_idx = 0;
+ while (actual_coll_level >= 0) {
+ coll_levels[collection_node_idx] = actual_coll_level;
+ if ((link_collection_nodes[collection_node_idx].NumberOfChildren > 0) &&
+ (coll_levels[link_collection_nodes[collection_node_idx].FirstChild] == -1)) {
+ actual_coll_level++;
+ coll_levels[collection_node_idx] = actual_coll_level;
+ if (max_coll_level < actual_coll_level) {
+ max_coll_level = actual_coll_level;
+ }
+ coll_number_of_direct_childs[collection_node_idx]++;
+ collection_node_idx = link_collection_nodes[collection_node_idx].FirstChild;
+ }
+ else if (link_collection_nodes[collection_node_idx].NextSibling != 0) {
+ coll_number_of_direct_childs[link_collection_nodes[collection_node_idx].Parent]++;
+ collection_node_idx = link_collection_nodes[collection_node_idx].NextSibling;
+ }
+ else {
+ actual_coll_level--;
+ if (actual_coll_level >= 0) {
+ collection_node_idx = link_collection_nodes[collection_node_idx].Parent;
+ }
+ }
+ }
+ }
+
+ // *********************************************************************************
+ // Propagate the bit range of each report from the child collections to their parent
+ // and store the merged result for the parent
+ // *********************************************************************************
+ for (int actual_coll_level = max_coll_level - 1; actual_coll_level >= 0; actual_coll_level--) {
+ for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
+ if (coll_levels[collection_node_idx] == actual_coll_level) {
+ USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild;
+ while (child_idx) {
+ for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
+ for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+ // Merge bit range from childs
+ if ((coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit != -1) &&
+ (coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit)) {
+ coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit;
+ }
+ if (coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit < coll_bit_range[child_idx][reportid_idx][rt_idx]->LastBit) {
+ coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = coll_bit_range[child_idx][reportid_idx][rt_idx]->LastBit;
+ }
+ child_idx = link_collection_nodes[child_idx].NextSibling;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // *************************************************************************************************
+ // Determine child collection order of the whole hierachy, based on previously determined bit ranges
+ // and store it this index coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX]
+ // *************************************************************************************************
+ USHORT **coll_child_order;
+ coll_child_order = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_child_order));
+ {
+ BOOLEAN *coll_parsed_flag;
+ coll_parsed_flag = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_parsed_flag[0]));
+ for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
+ coll_parsed_flag[collection_node_idx] = FALSE;
+ }
+ int actual_coll_level = 0;
+ USHORT collection_node_idx = 0;
+ while (actual_coll_level >= 0) {
+ if ((coll_number_of_direct_childs[collection_node_idx] != 0) &&
+ (coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] == FALSE)) {
+ coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] = TRUE;
+ coll_child_order[collection_node_idx] = malloc((coll_number_of_direct_childs[collection_node_idx]) * sizeof(*coll_child_order[0]));
+
+ {
+ // Create list of child collection indices
+ // sorted reverse to the order returned to HidP_GetLinkCollectionNodeschild
+ // which seems to match teh original order, as long as no bit position needs to be considered
+ USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild;
+ int child_count = coll_number_of_direct_childs[collection_node_idx] - 1;
+ coll_child_order[collection_node_idx][child_count] = child_idx;
+ while (link_collection_nodes[child_idx].NextSibling) {
+ child_count--;
+ child_idx = link_collection_nodes[child_idx].NextSibling;
+ coll_child_order[collection_node_idx][child_count] = child_idx;
+ }
+ }
+
+ if (coll_number_of_direct_childs[collection_node_idx] > 1) {
+ // Sort child collections indices by bit positions
+ for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+ for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
+ for (int child_idx = 1; child_idx < coll_number_of_direct_childs[collection_node_idx]; child_idx++) {
+ // since the coll_bit_range array is not sorted, we need to reference the collection index in
+ // our sorted coll_child_order array, and look up the corresponding bit ranges for comparing values to sort
+ int prev_coll_idx = coll_child_order[collection_node_idx][child_idx - 1];
+ int cur_coll_idx = coll_child_order[collection_node_idx][child_idx];
+ if ((coll_bit_range[prev_coll_idx][reportid_idx][rt_idx]->FirstBit != -1) &&
+ (coll_bit_range[cur_coll_idx][reportid_idx][rt_idx]->FirstBit != -1) &&
+ (coll_bit_range[prev_coll_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[cur_coll_idx][reportid_idx][rt_idx]->FirstBit)) {
+ // Swap position indices of the two compared child collections
+ USHORT idx_latch = coll_child_order[collection_node_idx][child_idx - 1];
+ coll_child_order[collection_node_idx][child_idx - 1] = coll_child_order[collection_node_idx][child_idx];
+ coll_child_order[collection_node_idx][child_idx] = idx_latch;
+ }
+ }
+ }
+ }
+ }
+ actual_coll_level++;
+ collection_node_idx = link_collection_nodes[collection_node_idx].FirstChild;
+ }
+ else if (link_collection_nodes[collection_node_idx].NextSibling != 0) {
+ collection_node_idx = link_collection_nodes[collection_node_idx].NextSibling;
+ }
+ else {
+ actual_coll_level--;
+ if (actual_coll_level >= 0) {
+ collection_node_idx = link_collection_nodes[collection_node_idx].Parent;
+ }
+ }
+ }
+ free(coll_parsed_flag);
+ }
+
+
+ // ***************************************************************************************
+ // Create sorted main_item_list containing all the Collection and CollectionEnd main items
+ // ***************************************************************************************
+ struct rd_main_item_node *main_item_list = NULL; // List root
+ // Lookup table to find the Collection items in the list by index
+ struct rd_main_item_node **coll_begin_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_begin_lookup));
+ struct rd_main_item_node **coll_end_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_end_lookup));
+ {
+ int *coll_last_written_child = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_last_written_child[0]));
+ for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
+ coll_last_written_child[collection_node_idx] = -1;
+ }
+
+ int actual_coll_level = 0;
+ USHORT collection_node_idx = 0;
+ struct rd_main_item_node *firstDelimiterNode = NULL;
+ struct rd_main_item_node *delimiterCloseNode = NULL;
+ coll_begin_lookup[0] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);
+ while (actual_coll_level >= 0) {
+ if ((coll_number_of_direct_childs[collection_node_idx] != 0) &&
+ (coll_last_written_child[collection_node_idx] == -1)) {
+ // Collection has child collections, but none is written to the list yet
+
+ coll_last_written_child[collection_node_idx] = coll_child_order[collection_node_idx][0];
+ collection_node_idx = coll_child_order[collection_node_idx][0];
+
+ // In a HID Report Descriptor, the first usage declared is the most preferred usage for the control.
+ // While the order in the WIN32 capabiliy strutures is the opposite:
+ // Here the preferred usage is the last aliased usage in the sequence.
+
+ if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode == NULL)) {
+ // Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output)
+ firstDelimiterNode = main_item_list;
+ coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &main_item_list);
+ coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_close, 0, &main_item_list);
+ delimiterCloseNode = main_item_list;
+ }
+ else {
+ // Normal not aliased collection
+ coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);
+ actual_coll_level++;
+ }
+
+
+ }
+ else if ((coll_number_of_direct_childs[collection_node_idx] > 1) &&
+ (coll_last_written_child[collection_node_idx] != coll_child_order[collection_node_idx][coll_number_of_direct_childs[collection_node_idx] - 1])) {
+ // Collection has child collections, and this is not the first child
+
+ int nextChild = 1;
+ while (coll_last_written_child[collection_node_idx] != coll_child_order[collection_node_idx][nextChild - 1]) {
+ nextChild++;
+ }
+ coll_last_written_child[collection_node_idx] = coll_child_order[collection_node_idx][nextChild];
+ collection_node_idx = coll_child_order[collection_node_idx][nextChild];
+
+ if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode == NULL)) {
+ // Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output)
+ firstDelimiterNode = main_item_list;
+ coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &main_item_list);
+ coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_close, 0, &main_item_list);
+ delimiterCloseNode = main_item_list;
+ }
+ else if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode != NULL)) {
+ coll_begin_lookup[collection_node_idx] = rd_insert_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &firstDelimiterNode);
+ }
+ else if (!link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode != NULL)) {
+ coll_begin_lookup[collection_node_idx] = rd_insert_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &firstDelimiterNode);
+ coll_begin_lookup[collection_node_idx] = rd_insert_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_open, 0, &firstDelimiterNode);
+ firstDelimiterNode = NULL;
+ main_item_list = delimiterCloseNode;
+ delimiterCloseNode = NULL; // Last entry of alias has .IsAlias == FALSE
+ }
+ if (!link_collection_nodes[collection_node_idx].IsAlias) {
+ coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);
+ actual_coll_level++;
+ }
+ }
+ else {
+ actual_coll_level--;
+ coll_end_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection_end, 0, &main_item_list);
+ collection_node_idx = link_collection_nodes[collection_node_idx].Parent;
+ }
+ }
+ free(coll_last_written_child);
+ }
+
+
+ // ****************************************************************
+ // Inserted Input/Output/Feature main items into the main_item_list
+ // in order of reconstructed bit positions
+ // ****************************************************************
+ for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+ // Add all value caps to node list
+ struct rd_main_item_node *firstDelimiterNode = NULL;
+ struct rd_main_item_node *delimiterCloseNode = NULL;
+ for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) {
+ struct rd_main_item_node *coll_begin = coll_begin_lookup[pp_data->caps[caps_idx].LinkCollection];
+ int first_bit, last_bit;
+ first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8 +
+ pp_data->caps[caps_idx].BitPosition;
+ last_bit = first_bit + pp_data->caps[caps_idx].ReportSize *
+ pp_data->caps[caps_idx].ReportCount - 1;
+
+ for (int child_idx = 0; child_idx < coll_number_of_direct_childs[pp_data->caps[caps_idx].LinkCollection]; child_idx++) {
+ // Determine in which section before/between/after child collection the item should be inserted
+ if (first_bit < coll_bit_range[coll_child_order[pp_data->caps[caps_idx].LinkCollection][child_idx]][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit)
+ {
+ // Note, that the default value for undefined coll_bit_range is -1, which can't be greater than the bit position
+ break;
+ }
+ coll_begin = coll_end_lookup[coll_child_order[pp_data->caps[caps_idx].LinkCollection][child_idx]];
+ }
+ struct rd_main_item_node *list_node;
+ list_node = rd_search_main_item_list_for_bit_position(first_bit, (rd_main_items) rt_idx, pp_data->caps[caps_idx].ReportID, &coll_begin);
+
+ // In a HID Report Descriptor, the first usage declared is the most preferred usage for the control.
+ // While the order in the WIN32 capabiliy strutures is the opposite:
+ // Here the preferred usage is the last aliased usage in the sequence.
+
+ if (pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode == NULL)) {
+ // Alliased Usage (First node in pp_data->caps -> Last entry in report descriptor output)
+ firstDelimiterNode = list_node;
+ rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_usage, pp_data->caps[caps_idx].ReportID, &list_node);
+ rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_close, pp_data->caps[caps_idx].ReportID, &list_node);
+ delimiterCloseNode = list_node;
+ } else if (pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode != NULL)) {
+ rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_usage, pp_data->caps[caps_idx].ReportID, &list_node);
+ }
+ else if (!pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode != NULL)) {
+ // Alliased Collection (Last node in pp_data->caps -> First entry in report descriptor output)
+ rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_usage, pp_data->caps[caps_idx].ReportID, &list_node);
+ rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_open, pp_data->caps[caps_idx].ReportID, &list_node);
+ firstDelimiterNode = NULL;
+ list_node = delimiterCloseNode;
+ delimiterCloseNode = NULL; // Last entry of alias has .IsAlias == FALSE
+ }
+ if (!pp_data->caps[caps_idx].IsAlias) {
+ rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, (rd_main_items) rt_idx, pp_data->caps[caps_idx].ReportID, &list_node);
+ }
+ }
+ }
+
+
+ // ***********************************************************
+ // Add const main items for padding to main_item_list
+ // -To fill all bit gaps
+ // -At each report end for 8bit padding
+ // Note that information about the padding at the report end,
+ // is not stored in the preparsed data, but in practice all
+ // report descriptors seem to have it, as assumed here.
+ // ***********************************************************
+ {
+ int last_bit_position[NUM_OF_HIDP_REPORT_TYPES][256];
+ struct rd_main_item_node *last_report_item_lookup[NUM_OF_HIDP_REPORT_TYPES][256];
+ for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+ for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
+ last_bit_position[rt_idx][reportid_idx] = -1;
+ last_report_item_lookup[rt_idx][reportid_idx] = NULL;
+ }
+ }
+
+ struct rd_main_item_node *list = main_item_list; // List root;
+
+ while (list->next != NULL)
+ {
+ if ((list->MainItemType >= rd_input) &&
+ (list->MainItemType <= rd_feature)) {
+ // INPUT, OUTPUT or FEATURE
+ if (list->FirstBit != -1) {
+ if ((last_bit_position[list->MainItemType][list->ReportID] + 1 != list->FirstBit) &&
+ (last_report_item_lookup[list->MainItemType][list->ReportID] != NULL) &&
+ (last_report_item_lookup[list->MainItemType][list->ReportID]->FirstBit != list->FirstBit) // Happens in case of IsMultipleItemsForArray for multiple dedicated usages for a multi-button array
+ ) {
+ struct rd_main_item_node *list_node = rd_search_main_item_list_for_bit_position(last_bit_position[list->MainItemType][list->ReportID], list->MainItemType, list->ReportID, &last_report_item_lookup[list->MainItemType][list->ReportID]);
+ rd_insert_main_item_node(last_bit_position[list->MainItemType][list->ReportID] + 1, list->FirstBit - 1, rd_item_node_padding, -1, 0, list->MainItemType, list->ReportID, &list_node);
+ }
+ last_bit_position[list->MainItemType][list->ReportID] = list->LastBit;
+ last_report_item_lookup[list->MainItemType][list->ReportID] = list;
+ }
+ }
+ list = list->next;
+ }
+ // Add 8 bit padding at each report end
+ for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+ for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
+ if (last_bit_position[rt_idx][reportid_idx] != -1) {
+ int padding = 8 - ((last_bit_position[rt_idx][reportid_idx] + 1) % 8);
+ if (padding < 8) {
+ // Insert padding item after item referenced in last_report_item_lookup
+ rd_insert_main_item_node(last_bit_position[rt_idx][reportid_idx] + 1, last_bit_position[rt_idx][reportid_idx] + padding, rd_item_node_padding, -1, 0, (rd_main_items) rt_idx, (unsigned char) reportid_idx, &last_report_item_lookup[rt_idx][reportid_idx]);
+ }
+ }
+ }
+ }
+ }
+
+
+ // ***********************************
+ // Encode the report descriptor output
+ // ***********************************
+ UCHAR last_report_id = 0;
+ USAGE last_usage_page = 0;
+ LONG last_physical_min = 0;// If both, Physical Minimum and Physical Maximum are 0, the logical limits should be taken as physical limits according USB HID spec 1.11 chapter 6.2.2.7
+ LONG last_physical_max = 0;
+ ULONG last_unit_exponent = 0; // If Unit Exponent is Undefined it should be considered as 0 according USB HID spec 1.11 chapter 6.2.2.7
+ ULONG last_unit = 0; // If the first nibble is 7, or second nibble of Unit is 0, the unit is None according USB HID spec 1.11 chapter 6.2.2.7
+ BOOLEAN inhibit_write_of_usage = FALSE; // Needed in case of delimited usage print, before the normal collection or cap
+ int report_count = 0;
+ while (main_item_list != NULL)
+ {
+ int rt_idx = main_item_list->MainItemType;
+ int caps_idx = main_item_list->CapsIndex;
+ if (main_item_list->MainItemType == rd_collection) {
+ if (last_usage_page != link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage) {
+ // Write "Usage Page" at the begin of a collection - except it refers the same table as wrote last
+ rd_write_short_item(rd_global_usage_page, link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage, &rpt_desc);
+ last_usage_page = link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage;
+ }
+ if (inhibit_write_of_usage) {
+ // Inhibit only once after DELIMITER statement
+ inhibit_write_of_usage = FALSE;
+ }
+ else {
+ // Write "Usage" of collection
+ rd_write_short_item(rd_local_usage, link_collection_nodes[main_item_list->CollectionIndex].LinkUsage, &rpt_desc);
+ }
+ // Write begin of "Collection"
+ rd_write_short_item(rd_main_collection, link_collection_nodes[main_item_list->CollectionIndex].CollectionType, &rpt_desc);
+ }
+ else if (main_item_list->MainItemType == rd_collection_end) {
+ // Write "End Collection"
+ rd_write_short_item(rd_main_collection_end, 0, &rpt_desc);
+ }
+ else if (main_item_list->MainItemType == rd_delimiter_open) {
+ if (main_item_list->CollectionIndex != -1) {
+ // Write "Usage Page" inside of a collection delmiter section
+ if (last_usage_page != link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage) {
+ rd_write_short_item(rd_global_usage_page, link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage, &rpt_desc);
+ last_usage_page = link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage;
+ }
+ }
+ else if (main_item_list->CapsIndex != 0) {
+ // Write "Usage Page" inside of a main item delmiter section
+ if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {
+ rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);
+ last_usage_page = pp_data->caps[caps_idx].UsagePage;
+ }
+ }
+ // Write "Delimiter Open"
+ rd_write_short_item(rd_local_delimiter, 1, &rpt_desc); // 1 = open set of aliased usages
+ }
+ else if (main_item_list->MainItemType == rd_delimiter_usage) {
+ if (main_item_list->CollectionIndex != -1) {
+ // Write aliased collection "Usage"
+ rd_write_short_item(rd_local_usage, link_collection_nodes[main_item_list->CollectionIndex].LinkUsage, &rpt_desc);
+ } if (main_item_list->CapsIndex != 0) {
+ // Write aliased main item range from "Usage Minimum" to "Usage Maximum"
+ if (pp_data->caps[caps_idx].IsRange) {
+ rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);
+ rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);
+ }
+ else {
+ // Write single aliased main item "Usage"
+ rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);
+ }
+ }
+ }
+ else if (main_item_list->MainItemType == rd_delimiter_close) {
+ // Write "Delimiter Close"
+ rd_write_short_item(rd_local_delimiter, 0, &rpt_desc); // 0 = close set of aliased usages
+ // Inhibit next usage write
+ inhibit_write_of_usage = TRUE;
+ }
+ else if (main_item_list->TypeOfNode == rd_item_node_padding) {
+ // Padding
+ // The preparsed data doesn't contain any information about padding. Therefore all undefined gaps
+ // in the reports are filled with the same style of constant padding.
+
+ // Write "Report Size" with number of padding bits
+ rd_write_short_item(rd_global_report_size, (main_item_list->LastBit - main_item_list->FirstBit + 1), &rpt_desc);
+
+ // Write "Report Count" for padding always as 1
+ rd_write_short_item(rd_global_report_count, 1, &rpt_desc);
+
+ if (rt_idx == HidP_Input) {
+ // Write "Input" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
+ rd_write_short_item(rd_main_input, 0x03, &rpt_desc); // Const / Abs
+ }
+ else if (rt_idx == HidP_Output) {
+ // Write "Output" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
+ rd_write_short_item(rd_main_output, 0x03, &rpt_desc); // Const / Abs
+ }
+ else if (rt_idx == HidP_Feature) {
+ // Write "Feature" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
+ rd_write_short_item(rd_main_feature, 0x03, &rpt_desc); // Const / Abs
+ }
+ report_count = 0;
+ }
+ else if (pp_data->caps[caps_idx].IsButtonCap) {
+ // Button
+ // (The preparsed data contain different data for 1 bit Button caps, than for parametric Value caps)
+
+ if (last_report_id != pp_data->caps[caps_idx].ReportID) {
+ // Write "Report ID" if changed
+ rd_write_short_item(rd_global_report_id, pp_data->caps[caps_idx].ReportID, &rpt_desc);
+ last_report_id = pp_data->caps[caps_idx].ReportID;
+ }
+
+ // Write "Usage Page" when changed
+ if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {
+ rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);
+ last_usage_page = pp_data->caps[caps_idx].UsagePage;
+ }
+
+ // Write only local report items for each cap, if ReportCount > 1
+ if (pp_data->caps[caps_idx].IsRange) {
+ report_count += (pp_data->caps[caps_idx].Range.DataIndexMax - pp_data->caps[caps_idx].Range.DataIndexMin);
+ }
+
+ if (inhibit_write_of_usage) {
+ // Inhibit only once after Delimiter - Reset flag
+ inhibit_write_of_usage = FALSE;
+ }
+ else {
+ if (pp_data->caps[caps_idx].IsRange) {
+ // Write range from "Usage Minimum" to "Usage Maximum"
+ rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);
+ rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);
+ }
+ else {
+ // Write single "Usage"
+ rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);
+ }
+ }
+
+ if (pp_data->caps[caps_idx].IsDesignatorRange) {
+ // Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum"
+ rd_write_short_item(rd_local_designator_minimum, pp_data->caps[caps_idx].Range.DesignatorMin, &rpt_desc);
+ rd_write_short_item(rd_local_designator_maximum, pp_data->caps[caps_idx].Range.DesignatorMax, &rpt_desc);
+ }
+ else if (pp_data->caps[caps_idx].NotRange.DesignatorIndex != 0) {
+ // Designator set 0 is a special descriptor set (of the HID Physical Descriptor),
+ // that specifies the number of additional descriptor sets.
+ // Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
+ // Write single "Designator Index"
+ rd_write_short_item(rd_local_designator_index, pp_data->caps[caps_idx].NotRange.DesignatorIndex, &rpt_desc);
+ }
+
+ if (pp_data->caps[caps_idx].IsStringRange) {
+ // Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum"
+ rd_write_short_item(rd_local_string_minimum, pp_data->caps[caps_idx].Range.StringMin, &rpt_desc);
+ rd_write_short_item(rd_local_string_maximum, pp_data->caps[caps_idx].Range.StringMax, &rpt_desc);
+ }
+ else if (pp_data->caps[caps_idx].NotRange.StringIndex != 0) {
+ // String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages,
+ // therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
+ // Write single "String Index"
+ rd_write_short_item(rd_local_string, pp_data->caps[caps_idx].NotRange.StringIndex, &rpt_desc);
+ }
+
+ if ((main_item_list->next != NULL) &&
+ ((int)main_item_list->next->MainItemType == rt_idx) &&
+ (main_item_list->next->TypeOfNode == rd_item_node_cap) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].IsButtonCap) &&
+ (!pp_data->caps[caps_idx].IsRange) && // This node in list is no array
+ (!pp_data->caps[main_item_list->next->CapsIndex].IsRange) && // Next node in list is no array
+ (pp_data->caps[main_item_list->next->CapsIndex].UsagePage == pp_data->caps[caps_idx].UsagePage) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].ReportID == pp_data->caps[caps_idx].ReportID) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].BitField == pp_data->caps[caps_idx].BitField)
+ ) {
+ if (main_item_list->next->FirstBit != main_item_list->FirstBit) {
+ // In case of IsMultipleItemsForArray for multiple dedicated usages for a multi-button array, the report count should be incremented
+
+ // Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields
+ report_count++;
+ }
+ }
+ else {
+
+ if ((pp_data->caps[caps_idx].Button.LogicalMin == 0) &&
+ (pp_data->caps[caps_idx].Button.LogicalMax == 0)) {
+ // While a HID report descriptor must always contain LogicalMinimum and LogicalMaximum,
+ // the preparsed data contain both fields set to zero, for the case of simple buttons
+ // Write "Logical Minimum" set to 0 and "Logical Maximum" set to 1
+ rd_write_short_item(rd_global_logical_minimum, 0, &rpt_desc);
+ rd_write_short_item(rd_global_logical_maximum, 1, &rpt_desc);
+ }
+ else {
+ // Write logical range from "Logical Minimum" to "Logical Maximum"
+ rd_write_short_item(rd_global_logical_minimum, pp_data->caps[caps_idx].Button.LogicalMin, &rpt_desc);
+ rd_write_short_item(rd_global_logical_maximum, pp_data->caps[caps_idx].Button.LogicalMax, &rpt_desc);
+ }
+
+ // Write "Report Size"
+ rd_write_short_item(rd_global_report_size, pp_data->caps[caps_idx].ReportSize, &rpt_desc);
+
+ // Write "Report Count"
+ if (!pp_data->caps[caps_idx].IsRange) {
+ // Variable bit field with one bit per button
+ // In case of multiple usages with the same items, only "Usage" is written per cap, and "Report Count" is incremented
+ rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount + report_count, &rpt_desc);
+ }
+ else {
+ // Button array of "Report Size" x "Report Count
+ rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount, &rpt_desc);
+ }
+
+
+ // Buttons have only 1 bit and therefore no physical limits/units -> Set to undefined state
+ if (last_physical_min != 0) {
+ // Write "Physical Minimum", but only if changed
+ last_physical_min = 0;
+ rd_write_short_item(rd_global_physical_minimum, last_physical_min, &rpt_desc);
+ }
+ if (last_physical_max != 0) {
+ // Write "Physical Maximum", but only if changed
+ last_physical_max = 0;
+ rd_write_short_item(rd_global_physical_maximum, last_physical_max, &rpt_desc);
+ }
+ if (last_unit_exponent != 0) {
+ // Write "Unit Exponent", but only if changed
+ last_unit_exponent = 0;
+ rd_write_short_item(rd_global_unit_exponent, last_unit_exponent, &rpt_desc);
+ }
+ if (last_unit != 0) {
+ // Write "Unit",but only if changed
+ last_unit = 0;
+ rd_write_short_item(rd_global_unit, last_unit, &rpt_desc);
+ }
+
+ // Write "Input" main item
+ if (rt_idx == HidP_Input) {
+ rd_write_short_item(rd_main_input, pp_data->caps[caps_idx].BitField, &rpt_desc);
+ }
+ // Write "Output" main item
+ else if (rt_idx == HidP_Output) {
+ rd_write_short_item(rd_main_output, pp_data->caps[caps_idx].BitField, &rpt_desc);
+ }
+ // Write "Feature" main item
+ else if (rt_idx == HidP_Feature) {
+ rd_write_short_item(rd_main_feature, pp_data->caps[caps_idx].BitField, &rpt_desc);
+ }
+ report_count = 0;
+ }
+ }
+ else {
+
+ if (last_report_id != pp_data->caps[caps_idx].ReportID) {
+ // Write "Report ID" if changed
+ rd_write_short_item(rd_global_report_id, pp_data->caps[caps_idx].ReportID, &rpt_desc);
+ last_report_id = pp_data->caps[caps_idx].ReportID;
+ }
+
+ // Write "Usage Page" if changed
+ if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {
+ rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);
+ last_usage_page = pp_data->caps[caps_idx].UsagePage;
+ }
+
+ if (inhibit_write_of_usage) {
+ // Inhibit only once after Delimiter - Reset flag
+ inhibit_write_of_usage = FALSE;
+ }
+ else {
+ if (pp_data->caps[caps_idx].IsRange) {
+ // Write usage range from "Usage Minimum" to "Usage Maximum"
+ rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);
+ rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);
+ }
+ else {
+ // Write single "Usage"
+ rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);
+ }
+ }
+
+ if (pp_data->caps[caps_idx].IsDesignatorRange) {
+ // Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum"
+ rd_write_short_item(rd_local_designator_minimum, pp_data->caps[caps_idx].Range.DesignatorMin, &rpt_desc);
+ rd_write_short_item(rd_local_designator_maximum, pp_data->caps[caps_idx].Range.DesignatorMax, &rpt_desc);
+ }
+ else if (pp_data->caps[caps_idx].NotRange.DesignatorIndex != 0) {
+ // Designator set 0 is a special descriptor set (of the HID Physical Descriptor),
+ // that specifies the number of additional descriptor sets.
+ // Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
+ // Write single "Designator Index"
+ rd_write_short_item(rd_local_designator_index, pp_data->caps[caps_idx].NotRange.DesignatorIndex, &rpt_desc);
+ }
+
+ if (pp_data->caps[caps_idx].IsStringRange) {
+ // Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum"
+ rd_write_short_item(rd_local_string_minimum, pp_data->caps[caps_idx].Range.StringMin, &rpt_desc);
+ rd_write_short_item(rd_local_string_maximum, pp_data->caps[caps_idx].Range.StringMax, &rpt_desc);
+ }
+ else if (pp_data->caps[caps_idx].NotRange.StringIndex != 0) {
+ // String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages,
+ // therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
+ // Write single "String Index"
+ rd_write_short_item(rd_local_string, pp_data->caps[caps_idx].NotRange.StringIndex, &rpt_desc);
+ }
+
+ if ((pp_data->caps[caps_idx].BitField & 0x02) != 0x02) {
+ // In case of an value array overwrite "Report Count"
+ pp_data->caps[caps_idx].ReportCount = pp_data->caps[caps_idx].Range.DataIndexMax - pp_data->caps[caps_idx].Range.DataIndexMin + 1;
+ }
+
+
+ // Print only local report items for each cap, if ReportCount > 1
+ if ((main_item_list->next != NULL) &&
+ ((int) main_item_list->next->MainItemType == rt_idx) &&
+ (main_item_list->next->TypeOfNode == rd_item_node_cap) &&
+ (!pp_data->caps[main_item_list->next->CapsIndex].IsButtonCap) &&
+ (!pp_data->caps[caps_idx].IsRange) && // This node in list is no array
+ (!pp_data->caps[main_item_list->next->CapsIndex].IsRange) && // Next node in list is no array
+ (pp_data->caps[main_item_list->next->CapsIndex].UsagePage == pp_data->caps[caps_idx].UsagePage) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].NotButton.LogicalMin == pp_data->caps[caps_idx].NotButton.LogicalMin) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].NotButton.LogicalMax == pp_data->caps[caps_idx].NotButton.LogicalMax) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].NotButton.PhysicalMin == pp_data->caps[caps_idx].NotButton.PhysicalMin) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].NotButton.PhysicalMax == pp_data->caps[caps_idx].NotButton.PhysicalMax) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].UnitsExp == pp_data->caps[caps_idx].UnitsExp) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].Units == pp_data->caps[caps_idx].Units) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].ReportSize == pp_data->caps[caps_idx].ReportSize) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].ReportID == pp_data->caps[caps_idx].ReportID) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].BitField == pp_data->caps[caps_idx].BitField) &&
+ (pp_data->caps[main_item_list->next->CapsIndex].ReportCount == 1) &&
+ (pp_data->caps[caps_idx].ReportCount == 1)
+ ) {
+ // Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields
+ report_count++;
+ }
+ else {
+ // Value
+
+ // Write logical range from "Logical Minimum" to "Logical Maximum"
+ rd_write_short_item(rd_global_logical_minimum, pp_data->caps[caps_idx].NotButton.LogicalMin, &rpt_desc);
+ rd_write_short_item(rd_global_logical_maximum, pp_data->caps[caps_idx].NotButton.LogicalMax, &rpt_desc);
+
+ if ((last_physical_min != pp_data->caps[caps_idx].NotButton.PhysicalMin) ||
+ (last_physical_max != pp_data->caps[caps_idx].NotButton.PhysicalMax)) {
+ // Write range from "Physical Minimum" to " Physical Maximum", but only if one of them changed
+ rd_write_short_item(rd_global_physical_minimum, pp_data->caps[caps_idx].NotButton.PhysicalMin, &rpt_desc);
+ last_physical_min = pp_data->caps[caps_idx].NotButton.PhysicalMin;
+ rd_write_short_item(rd_global_physical_maximum, pp_data->caps[caps_idx].NotButton.PhysicalMax, &rpt_desc);
+ last_physical_max = pp_data->caps[caps_idx].NotButton.PhysicalMax;
+ }
+
+
+ if (last_unit_exponent != pp_data->caps[caps_idx].UnitsExp) {
+ // Write "Unit Exponent", but only if changed
+ rd_write_short_item(rd_global_unit_exponent, pp_data->caps[caps_idx].UnitsExp, &rpt_desc);
+ last_unit_exponent = pp_data->caps[caps_idx].UnitsExp;
+ }
+
+ if (last_unit != pp_data->caps[caps_idx].Units) {
+ // Write physical "Unit", but only if changed
+ rd_write_short_item(rd_global_unit, pp_data->caps[caps_idx].Units, &rpt_desc);
+ last_unit = pp_data->caps[caps_idx].Units;
+ }
+
+ // Write "Report Size"
+ rd_write_short_item(rd_global_report_size, pp_data->caps[caps_idx].ReportSize, &rpt_desc);
+
+ // Write "Report Count"
+ rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount + report_count, &rpt_desc);
+
+ if (rt_idx == HidP_Input) {
+ // Write "Input" main item
+ rd_write_short_item(rd_main_input, pp_data->caps[caps_idx].BitField, &rpt_desc);
+ }
+ else if (rt_idx == HidP_Output) {
+ // Write "Output" main item
+ rd_write_short_item(rd_main_output, pp_data->caps[caps_idx].BitField, &rpt_desc);
+ }
+ else if (rt_idx == HidP_Feature) {
+ // Write "Feature" main item
+ rd_write_short_item(rd_main_feature, pp_data->caps[caps_idx].BitField, &rpt_desc);
+ }
+ report_count = 0;
+ }
+ }
+
+ // Go to next item in main_item_list and free the memory of the actual item
+ struct rd_main_item_node *main_item_list_prev = main_item_list;
+ main_item_list = main_item_list->next;
+ free(main_item_list_prev);
+ }
+
+ // Free multidimensionable array: coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE]
+ // Free multidimensionable array: coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX]
+ for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
+ for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
+ for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
+ free(coll_bit_range[collection_node_idx][reportid_idx][rt_idx]);
+ }
+ free(coll_bit_range[collection_node_idx][reportid_idx]);
+ }
+ free(coll_bit_range[collection_node_idx]);
+ if (coll_number_of_direct_childs[collection_node_idx] != 0) free(coll_child_order[collection_node_idx]);
+ }
+ free(coll_bit_range);
+ free(coll_child_order);
+
+ // Free one dimensional arrays
+ free(coll_begin_lookup);
+ free(coll_end_lookup);
+ free(coll_levels);
+ free(coll_number_of_direct_childs);
+
+ return (int) rpt_desc.byte_idx;
+}
diff --git a/src/hidapi/windows/hidapi_descriptor_reconstruct.h b/src/hidapi/windows/hidapi_descriptor_reconstruct.h
new file mode 100644
index 0000000..2ec8b20
--- /dev/null
+++ b/src/hidapi/windows/hidapi_descriptor_reconstruct.h
@@ -0,0 +1,238 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ libusb/hidapi Team
+
+ Copyright 2022, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+ https://github.com/libusb/hidapi .
+********************************************************/
+#ifndef HIDAPI_DESCRIPTOR_RECONSTRUCT_H__
+#define HIDAPI_DESCRIPTOR_RECONSTRUCT_H__
+
+#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
+/* Do not warn about wcsncpy usage.
+ https://docs.microsoft.com/cpp/c-runtime-library/security-features-in-the-crt */
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include "hidapi_winapi.h"
+
+#if _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4200)
+#pragma warning(disable: 4201)
+#endif
+
+#include <windows.h>
+
+#include "hidapi_hidsdi.h"
+
+#define NUM_OF_HIDP_REPORT_TYPES 3
+
+typedef enum rd_items_ {
+ rd_main_input = 0x80, /* 1000 00 nn */
+ rd_main_output = 0x90, /* 1001 00 nn */
+ rd_main_feature = 0xB0, /* 1011 00 nn */
+ rd_main_collection = 0xA0, /* 1010 00 nn */
+ rd_main_collection_end = 0xC0, /* 1100 00 nn */
+ rd_global_usage_page = 0x04, /* 0000 01 nn */
+ rd_global_logical_minimum = 0x14, /* 0001 01 nn */
+ rd_global_logical_maximum = 0x24, /* 0010 01 nn */
+ rd_global_physical_minimum = 0x34, /* 0011 01 nn */
+ rd_global_physical_maximum = 0x44, /* 0100 01 nn */
+ rd_global_unit_exponent = 0x54, /* 0101 01 nn */
+ rd_global_unit = 0x64, /* 0110 01 nn */
+ rd_global_report_size = 0x74, /* 0111 01 nn */
+ rd_global_report_id = 0x84, /* 1000 01 nn */
+ rd_global_report_count = 0x94, /* 1001 01 nn */
+ rd_global_push = 0xA4, /* 1010 01 nn */
+ rd_global_pop = 0xB4, /* 1011 01 nn */
+ rd_local_usage = 0x08, /* 0000 10 nn */
+ rd_local_usage_minimum = 0x18, /* 0001 10 nn */
+ rd_local_usage_maximum = 0x28, /* 0010 10 nn */
+ rd_local_designator_index = 0x38, /* 0011 10 nn */
+ rd_local_designator_minimum = 0x48, /* 0100 10 nn */
+ rd_local_designator_maximum = 0x58, /* 0101 10 nn */
+ rd_local_string = 0x78, /* 0111 10 nn */
+ rd_local_string_minimum = 0x88, /* 1000 10 nn */
+ rd_local_string_maximum = 0x98, /* 1001 10 nn */
+ rd_local_delimiter = 0xA8 /* 1010 10 nn */
+} rd_items;
+
+typedef enum rd_main_items_ {
+ rd_input = HidP_Input,
+ rd_output = HidP_Output,
+ rd_feature = HidP_Feature,
+ rd_collection,
+ rd_collection_end,
+ rd_delimiter_open,
+ rd_delimiter_usage,
+ rd_delimiter_close,
+} rd_main_items;
+
+typedef struct rd_bit_range_ {
+ int FirstBit;
+ int LastBit;
+} rd_bit_range;
+
+typedef enum rd_item_node_type_ {
+ rd_item_node_cap,
+ rd_item_node_padding,
+ rd_item_node_collection,
+} rd_node_type;
+
+struct rd_main_item_node {
+ int FirstBit; /* Position of first bit in report (counting from 0) */
+ int LastBit; /* Position of last bit in report (counting from 0) */
+ rd_node_type TypeOfNode; /* Information if caps index refers to the array of button caps, value caps,
+ or if the node is just a padding element to fill unused bit positions.
+ The node can also be a collection node without any bits in the report. */
+ int CapsIndex; /* Index in the array of caps */
+ int CollectionIndex; /* Index in the array of link collections */
+ rd_main_items MainItemType; /* Input, Output, Feature, Collection or Collection End */
+ unsigned char ReportID;
+ struct rd_main_item_node* next;
+};
+
+typedef struct hid_pp_caps_info_ {
+ USHORT FirstCap;
+ USHORT NumberOfCaps; // Includes empty caps after LastCap
+ USHORT LastCap;
+ USHORT ReportByteLength;
+} hid_pp_caps_info, *phid_pp_caps_info;
+
+typedef struct hid_pp_link_collection_node_ {
+ USAGE LinkUsage;
+ USAGE LinkUsagePage;
+ USHORT Parent;
+ USHORT NumberOfChildren;
+ USHORT NextSibling;
+ USHORT FirstChild;
+ ULONG CollectionType : 8;
+ ULONG IsAlias : 1;
+ ULONG Reserved : 23;
+ // Same as the public API structure HIDP_LINK_COLLECTION_NODE, but without PVOID UserContext at the end
+} hid_pp_link_collection_node, *phid_pp_link_collection_node;
+
+typedef struct hidp_unknown_token_ {
+ UCHAR Token; /* Specifies the one-byte prefix of a global item. */
+ UCHAR Reserved[3];
+ ULONG BitField; /* Specifies the data part of the global item. */
+} hidp_unknown_token, * phidp_unknown_token;
+
+typedef struct hid_pp_cap_ {
+ USAGE UsagePage;
+ UCHAR ReportID;
+ UCHAR BitPosition;
+ USHORT ReportSize; // WIN32 term for this is BitSize
+ USHORT ReportCount;
+ USHORT BytePosition;
+ USHORT BitCount;
+ ULONG BitField;
+ USHORT NextBytePosition;
+ USHORT LinkCollection;
+ USAGE LinkUsagePage;
+ USAGE LinkUsage;
+
+ // Start of 8 Flags in one byte
+ BOOLEAN IsMultipleItemsForArray:1;
+
+ BOOLEAN IsPadding:1;
+ BOOLEAN IsButtonCap:1;
+ BOOLEAN IsAbsolute:1;
+ BOOLEAN IsRange:1;
+ BOOLEAN IsAlias:1; // IsAlias is set to TRUE in the first n-1 capability structures added to the capability array. IsAlias set to FALSE in the nth capability structure.
+ BOOLEAN IsStringRange:1;
+ BOOLEAN IsDesignatorRange:1;
+ // End of 8 Flags in one byte
+ BOOLEAN Reserved1[3];
+
+ hidp_unknown_token UnknownTokens[4]; // 4 x 8 Byte
+
+ union {
+ struct {
+ USAGE UsageMin;
+ USAGE UsageMax;
+ USHORT StringMin;
+ USHORT StringMax;
+ USHORT DesignatorMin;
+ USHORT DesignatorMax;
+ USHORT DataIndexMin;
+ USHORT DataIndexMax;
+ } Range;
+ struct {
+ USAGE Usage;
+ USAGE Reserved1;
+ USHORT StringIndex;
+ USHORT Reserved2;
+ USHORT DesignatorIndex;
+ USHORT Reserved3;
+ USHORT DataIndex;
+ USHORT Reserved4;
+ } NotRange;
+ };
+ union {
+ struct {
+ LONG LogicalMin;
+ LONG LogicalMax;
+ } Button;
+ struct {
+ BOOLEAN HasNull;
+ UCHAR Reserved4[3];
+ LONG LogicalMin;
+ LONG LogicalMax;
+ LONG PhysicalMin;
+ LONG PhysicalMax;
+ } NotButton;
+ };
+ ULONG Units;
+ ULONG UnitsExp;
+
+} hid_pp_cap, *phid_pp_cap;
+
+typedef struct hidp_preparsed_data_ {
+ UCHAR MagicKey[8];
+ USAGE Usage;
+ USAGE UsagePage;
+ USHORT Reserved[2];
+
+ // CAPS structure for Input, Output and Feature
+ hid_pp_caps_info caps_info[3];
+
+ USHORT FirstByteOfLinkCollectionArray;
+ USHORT NumberLinkCollectionNodes;
+
+#if defined(__MINGW32__) || defined(__CYGWIN__)
+ // MINGW fails with: Flexible array member in union not supported
+ // Solution: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
+ union {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+ hid_pp_cap caps[0];
+ hid_pp_link_collection_node LinkCollectionArray[0];
+#pragma GCC diagnostic pop
+ };
+#else
+ union {
+ hid_pp_cap caps[];
+ hid_pp_link_collection_node LinkCollectionArray[];
+ };
+#endif
+
+} hidp_preparsed_data;
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif
diff --git a/src/hidapi/windows/hidapi_hidpi.h b/src/hidapi/windows/hidapi_hidpi.h
index 343ddeb..75a5812 100644
--- a/src/hidapi/windows/hidapi_hidpi.h
+++ b/src/hidapi/windows/hidapi_hidpi.h
@@ -29,6 +29,13 @@
/* This part of the header mimics hidpi.h,
but only what is used by HIDAPI */
+typedef enum _HIDP_REPORT_TYPE
+{
+ HidP_Input,
+ HidP_Output,
+ HidP_Feature
+} HIDP_REPORT_TYPE;
+
typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA;
typedef struct _HIDP_CAPS
diff --git a/src/hidapi/windows/hidapi_hidsdi.h b/src/hidapi/windows/hidapi_hidsdi.h
index a820d96..453f899 100644
--- a/src/hidapi/windows/hidapi_hidsdi.h
+++ b/src/hidapi/windows/hidapi_hidsdi.h
@@ -40,6 +40,8 @@
USHORT VersionNumber;
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
+typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA;
+
typedef void (__stdcall *HidD_GetHidGuid_)(LPGUID hid_guid);
typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib);
typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len);
@@ -52,7 +54,6 @@
typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data);
typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data);
typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers);
-typedef BOOLEAN (__stdcall *HidD_SetOutputReport_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
#endif
diff --git a/src/hidapi/windows/hidapi_winapi.h b/src/hidapi/windows/hidapi_winapi.h
new file mode 100644
index 0000000..43071f5
--- /dev/null
+++ b/src/hidapi/windows/hidapi_winapi.h
@@ -0,0 +1,74 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ libusb/hidapi Team
+
+ Copyright 2022, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+ https://github.com/libusb/hidapi .
+********************************************************/
+
+/** @file
+ * @defgroup API hidapi API
+ *
+ * Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+ */
+
+#ifndef HIDAPI_WINAPI_H__
+#define HIDAPI_WINAPI_H__
+
+#include <stdint.h>
+
+#include <guiddef.h>
+
+#include "hidapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /** @brief Get the container ID for a HID device.
+
+ Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
+
+ This function returns the `DEVPKEY_Device_ContainerId` property of
+ the given device. This can be used to correlate different
+ interfaces/ports on the same hardware device.
+
+ @ingroup API
+ @param dev A device handle returned from hid_open().
+ @param guid The device's container ID on return.
+
+ @returns
+ This function returns 0 on success and -1 on error.
+ */
+ int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *container_id);
+
+ /**
+ * @brief Reconstructs a HID Report Descriptor from a Win32 HIDP_PREPARSED_DATA structure.
+ * This reconstructed report descriptor is logical identical to the real report descriptor,
+ * but not byte wise identical.
+ *
+ * @param[in] hidp_preparsed_data Pointer to the HIDP_PREPARSED_DATA to read, i.e.: the value of PHIDP_PREPARSED_DATA,
+ * as returned by HidD_GetPreparsedData WinAPI function.
+ * @param buf Pointer to the buffer where the report descriptor should be stored.
+ * @param[in] buf_size Size of the buffer. The recommended size for the buffer is @ref HID_API_MAX_REPORT_DESCRIPTOR_SIZE bytes.
+ *
+ * @return Returns size of reconstructed report descriptor if successful, -1 for error.
+ */
+ int HID_API_EXPORT_CALL hid_winapi_descriptor_reconstruct_pp_data(void *hidp_preparsed_data, unsigned char *buf, size_t buf_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/hidapi/windows/hidtest.vcproj b/src/hidapi/windows/hidtest.vcproj
index cf71195..2918fc4 100644
--- a/src/hidapi/windows/hidtest.vcproj
+++ b/src/hidapi/windows/hidtest.vcproj
@@ -1,196 +1,196 @@
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="9.00"
- Name="hidtest"
- ProjectGUID="{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}"
- RootNamespace="hidtest"
- TargetFrameworkVersion="196613"
- >
- <Platforms>
- <Platform
- Name="Win32"
- />
- </Platforms>
- <ToolFiles>
- </ToolFiles>
- <Configurations>
- <Configuration
- Name="Debug|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
- ConfigurationType="1"
- CharacterSet="2"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- AdditionalIncludeDirectories="..\hidapi"
- MinimalRebuild="true"
- BasicRuntimeChecks="3"
- RuntimeLibrary="3"
- WarningLevel="3"
- DebugInformationFormat="4"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="hidapi.lib"
- AdditionalLibraryDirectories="..\windows\Debug"
- GenerateDebugInformation="true"
- SubSystem="1"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- Description="Copying hidapi.dll to the local direcotry."
- CommandLine=""
- />
- </Configuration>
- <Configuration
- Name="Release|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
- ConfigurationType="1"
- CharacterSet="2"
- WholeProgramOptimization="1"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- EnableIntrinsicFunctions="true"
- AdditionalIncludeDirectories="..\hidapi"
- RuntimeLibrary="2"
- EnableFunctionLevelLinking="true"
- WarningLevel="3"
- DebugInformationFormat="3"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="hidapi.lib"
- AdditionalLibraryDirectories="..\windows\Release"
- GenerateDebugInformation="true"
- SubSystem="1"
- OptimizeReferences="2"
- EnableCOMDATFolding="2"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- Description="Copying hidapi.dll to the local direcotry."
- CommandLine=""
- />
- </Configuration>
- </Configurations>
- <References>
- </References>
- <Files>
- <Filter
- Name="Source Files"
- Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
- UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
- >
- <File
- RelativePath="..\hidtest\hidtest.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Header Files"
- Filter="h;hpp;hxx;hm;inl;inc;xsd"
- UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
- >
- </Filter>
- <Filter
- Name="Resource Files"
- Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
- UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
- >
- </Filter>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="hidtest"
+ ProjectGUID="{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}"
+ RootNamespace="hidtest"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\hidapi;."
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="hidapi.lib"
+ AdditionalLibraryDirectories="..\windows\Debug"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ Description="Copying hidapi.dll to the local directory."
+ CommandLine=""
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="..\hidapi;."
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="hidapi.lib"
+ AdditionalLibraryDirectories="..\windows\Release"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ Description="Copying hidapi.dll to the local directory."
+ CommandLine=""
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\hidtest\test.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src/hidapi/windows/hidtest.vcxproj b/src/hidapi/windows/hidtest.vcxproj
new file mode 100644
index 0000000..a468d8b
--- /dev/null
+++ b/src/hidapi/windows/hidtest.vcxproj
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}</ProjectGuid>
+ <RootNamespace>hidtest</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='11'">v110</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='12'">v120</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='14'">v140</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='15'">v141</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='16'">v142</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='17'">v143</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='11'">v110</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='12'">v120</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='14'">v140</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='15'">v141</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='16'">v142</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='17'">v143</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='11'">v110</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='12'">v120</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='14'">v140</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='15'">v141</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='16'">v142</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='17'">v143</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='11'">v110</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='12'">v120</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='14'">v140</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='15'">v141</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='16'">v142</PlatformToolset>
+ <PlatformToolset Condition="'$(VisualStudioVersion)'=='17'">v143</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>14.0.25431.1</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\hidapi;.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>hidapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(SolutionDir)$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\hidapi;.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>hidapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..\hidapi;.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>hidapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(SolutionDir)$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..\hidapi;.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>hidapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\hidtest\test.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="hidapi.vcxproj">
+ <Project>{a107c21c-418a-4697-bb10-20c3aa60e2e4}</Project>
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/src/hidapi/windows/pp_data_dump/CMakeLists.txt b/src/hidapi/windows/pp_data_dump/CMakeLists.txt
new file mode 100644
index 0000000..f017de9
--- /dev/null
+++ b/src/hidapi/windows/pp_data_dump/CMakeLists.txt
@@ -0,0 +1,15 @@
+project(pp_data_dump C)
+
+add_executable(pp_data_dump pp_data_dump.c)
+set_target_properties(pp_data_dump
+ PROPERTIES
+ C_STANDARD 11
+ C_STANDARD_REQUIRED TRUE
+)
+target_link_libraries(pp_data_dump
+ PRIVATE hidapi_winapi
+)
+
+install(TARGETS pp_data_dump
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+)
diff --git a/src/hidapi/windows/pp_data_dump/README.md b/src/hidapi/windows/pp_data_dump/README.md
new file mode 100644
index 0000000..a0989cd
--- /dev/null
+++ b/src/hidapi/windows/pp_data_dump/README.md
@@ -0,0 +1,122 @@
+## pp_data_dump.exe for Windows
+
+
+pp_data_dump.exe is a small command line tool for Windows, which dumps the content of the [Preparsed Data](https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/preparsed-data) structure, provided by the Windows HID subsystem, into a file.
+
+The generated file is in a text format, which is readable for human, as by the hid_report_reconstructor_test.exe unit test executable of the HIDAPI project. This unit test allows it to test the HIDAPIs report descriptor reconstructor - offline, without the hardware device connected.
+
+pp_data_dump.exe has no arguments, just connect you HID device and execute pp_data_dump.exe. It will generate one file with the name
+```
+<vendor_id>_<product_id>_<usage>_<usage_table>.pp_data
+```
+for each top-level collection, of each connected HID device.
+
+
+## File content
+
+The content of such a .pp_data file looks like the struct, which HIDAPI uses internally to represent the Preparsed Data.
+
+*NOTE:
+Windows parses HID report descriptors into opaque `_HIDP_PREPARSED_DATA` objects.
+The internal structure of `_HIDP_PREPARSED_DATA` is reserved for internal system use.\
+Microsoft doesn't document this structure. [hid_preparsed_data.cc](https://chromium.googlesource.com/chromium/src/+/73fdaaf605bb60caf34d5f30bb84a417688aa528/services/device/hid/hid_preparsed_data.cc) is taken as a reference for its parsing.*
+
+```
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xB010
+dev->manufacturer_string = "Logitech"
+dev->product_string = "Logitech Bluetooth Wireless Mouse"
+dev->release_number = 0x0000
+dev->interface_number = -1
+dev->usage = 0x0001
+dev->usage_page = 0x000C
+dev->path = "\\?\hid#{00001124-0000-1000-8000-00805f9b34fb}_vid&0002046d_pid&b010&col02#8&1cf1c1b9&3&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0001
+pp_data->UsagePage = 0x000C
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 1
+pp_data->caps_info[0]->NumberOfCaps = 1
+pp_data->caps_info[0]->ReportByteLength = 2
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 1
+pp_data->caps_info[1]->LastCap = 1
+pp_data->caps_info[1]->NumberOfCaps = 0
+pp_data->caps_info[1]->ReportByteLength = 0
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 1
+pp_data->caps_info[2]->LastCap = 1
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x0068
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x0006
+pp_data->cap[0]->ReportID = 0x03
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 8
+pp_data->cap[0]->ReportCount = 1
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 8
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0002
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0x000C
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 0
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0020
+pp_data->cap[0]->NotRange.Reserved1 = 0x0020
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->NotButton.HasNull = 0
+pp_data->cap[0]->NotButton.Reserved4 = 0x000000
+pp_data->cap[0]->NotButton.LogicalMin = 0
+pp_data->cap[0]->NotButton.LogicalMax = 100
+pp_data->cap[0]->NotButton.PhysicalMin = 0
+pp_data->cap[0]->NotButton.PhysicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x000C
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
+```
\ No newline at end of file
diff --git a/src/hidapi/windows/pp_data_dump/pp_data_dump.c b/src/hidapi/windows/pp_data_dump/pp_data_dump.c
new file mode 100644
index 0000000..561dc96
--- /dev/null
+++ b/src/hidapi/windows/pp_data_dump/pp_data_dump.c
@@ -0,0 +1,238 @@
+
+
+#include <hid.c>
+#include <../windows/hidapi_descriptor_reconstruct.h>
+
+#include <hidapi.h>
+
+#if defined(__MINGW32__)
+#pragma GCC diagnostic ignored "-Wformat"
+#pragma GCC diagnostic ignored "-Wformat-extra-args"
+#endif
+
+void dump_hid_pp_cap(FILE* file, phid_pp_cap pp_cap, unsigned int cap_idx) {
+ fprintf(file, "pp_data->cap[%d]->UsagePage = 0x%04hX\n", cap_idx, pp_cap->UsagePage);
+ fprintf(file, "pp_data->cap[%d]->ReportID = 0x%02hhX\n", cap_idx, pp_cap->ReportID);
+ fprintf(file, "pp_data->cap[%d]->BitPosition = %hhu\n", cap_idx, pp_cap->BitPosition);
+ fprintf(file, "pp_data->cap[%d]->BitSize = %hu\n", cap_idx, pp_cap->ReportSize);
+ fprintf(file, "pp_data->cap[%d]->ReportCount = %hu\n", cap_idx, pp_cap->ReportCount);
+ fprintf(file, "pp_data->cap[%d]->BytePosition = 0x%04hX\n", cap_idx, pp_cap->BytePosition);
+ fprintf(file, "pp_data->cap[%d]->BitCount = %hu\n", cap_idx, pp_cap->BitCount);
+ fprintf(file, "pp_data->cap[%d]->BitField = 0x%02lX\n", cap_idx, pp_cap->BitField);
+ fprintf(file, "pp_data->cap[%d]->NextBytePosition = 0x%04hX\n", cap_idx, pp_cap->NextBytePosition);
+ fprintf(file, "pp_data->cap[%d]->LinkCollection = 0x%04hX\n", cap_idx, pp_cap->LinkCollection);
+ fprintf(file, "pp_data->cap[%d]->LinkUsagePage = 0x%04hX\n", cap_idx, pp_cap->LinkUsagePage);
+ fprintf(file, "pp_data->cap[%d]->LinkUsage = 0x%04hX\n", cap_idx, pp_cap->LinkUsage);
+
+ // 8 Flags in one byte
+ fprintf(file, "pp_data->cap[%d]->IsMultipleItemsForArray = %hhu\n", cap_idx, pp_cap->IsMultipleItemsForArray);
+ fprintf(file, "pp_data->cap[%d]->IsButtonCap = %hhu\n", cap_idx, pp_cap->IsButtonCap);
+ fprintf(file, "pp_data->cap[%d]->IsPadding = %hhu\n", cap_idx, pp_cap->IsPadding);
+ fprintf(file, "pp_data->cap[%d]->IsAbsolute = %hhu\n", cap_idx, pp_cap->IsAbsolute);
+ fprintf(file, "pp_data->cap[%d]->IsRange = %hhu\n", cap_idx, pp_cap->IsRange);
+ fprintf(file, "pp_data->cap[%d]->IsAlias = %hhu\n", cap_idx, pp_cap->IsAlias);
+ fprintf(file, "pp_data->cap[%d]->IsStringRange = %hhu\n", cap_idx, pp_cap->IsStringRange);
+ fprintf(file, "pp_data->cap[%d]->IsDesignatorRange = %hhu\n", cap_idx, pp_cap->IsDesignatorRange);
+
+ fprintf(file, "pp_data->cap[%d]->Reserved1 = 0x%02hhX%02hhX%02hhX\n", cap_idx, pp_cap->Reserved1[0], pp_cap->Reserved1[1], pp_cap->Reserved1[2]);
+
+ for (int token_idx = 0; token_idx < 4; token_idx++) {
+ fprintf(file, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d].Token = 0x%02hhX\n", cap_idx, token_idx, pp_cap->UnknownTokens[token_idx].Token);
+ fprintf(file, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d].Reserved = 0x%02hhX%02hhX%02hhX\n", cap_idx, token_idx, pp_cap->UnknownTokens[token_idx].Reserved[0], pp_cap->UnknownTokens[token_idx].Reserved[1], pp_cap->UnknownTokens[token_idx].Reserved[2]);
+ fprintf(file, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d].BitField = 0x%08lX\n", cap_idx, token_idx, pp_cap->UnknownTokens[token_idx].BitField);
+ }
+
+ if (pp_cap->IsRange) {
+ fprintf(file, "pp_data->cap[%d]->Range.UsageMin = 0x%04hX\n", cap_idx, pp_cap->Range.UsageMin);
+ fprintf(file, "pp_data->cap[%d]->Range.UsageMax = 0x%04hX\n", cap_idx, pp_cap->Range.UsageMax);
+ fprintf(file, "pp_data->cap[%d]->Range.StringMin = %hu\n", cap_idx, pp_cap->Range.StringMin);
+ fprintf(file, "pp_data->cap[%d]->Range.StringMax = %hu\n", cap_idx, pp_cap->Range.StringMax);
+ fprintf(file, "pp_data->cap[%d]->Range.DesignatorMin = %hu\n", cap_idx, pp_cap->Range.DesignatorMin);
+ fprintf(file, "pp_data->cap[%d]->Range.DesignatorMax = %hu\n", cap_idx, pp_cap->Range.DesignatorMax);
+ fprintf(file, "pp_data->cap[%d]->Range.DataIndexMin = %hu\n", cap_idx, pp_cap->Range.DataIndexMin);
+ fprintf(file, "pp_data->cap[%d]->Range.DataIndexMax = %hu\n", cap_idx, pp_cap->Range.DataIndexMax);
+ }
+ else {
+ fprintf(file, "pp_data->cap[%d]->NotRange.Usage = 0x%04hX\n", cap_idx, pp_cap->NotRange.Usage);
+ fprintf(file, "pp_data->cap[%d]->NotRange.Reserved1 = 0x%04hX\n", cap_idx, pp_cap->NotRange.Reserved1);
+ fprintf(file, "pp_data->cap[%d]->NotRange.StringIndex = %hu\n", cap_idx, pp_cap->NotRange.StringIndex);
+ fprintf(file, "pp_data->cap[%d]->NotRange.Reserved2 = %hu\n", cap_idx, pp_cap->NotRange.Reserved2);
+ fprintf(file, "pp_data->cap[%d]->NotRange.DesignatorIndex = %hu\n", cap_idx, pp_cap->NotRange.DesignatorIndex);
+ fprintf(file, "pp_data->cap[%d]->NotRange.Reserved3 = %hu\n", cap_idx, pp_cap->NotRange.Reserved3);
+ fprintf(file, "pp_data->cap[%d]->NotRange.DataIndex = %hu\n", cap_idx, pp_cap->NotRange.DataIndex);
+ fprintf(file, "pp_data->cap[%d]->NotRange.Reserved4 = %hu\n", cap_idx, pp_cap->NotRange.Reserved4);
+ }
+
+ if (pp_cap->IsButtonCap) {
+ fprintf(file, "pp_data->cap[%d]->Button.LogicalMin = %ld\n", cap_idx, pp_cap->Button.LogicalMin);
+ fprintf(file, "pp_data->cap[%d]->Button.LogicalMax = %ld\n", cap_idx, pp_cap->Button.LogicalMax);
+ }
+ else
+ {
+ fprintf(file, "pp_data->cap[%d]->NotButton.HasNull = %hhu\n", cap_idx, pp_cap->NotButton.HasNull);
+ fprintf(file, "pp_data->cap[%d]->NotButton.Reserved4 = 0x%02hhX%02hhX%02hhX\n", cap_idx, pp_cap->NotButton.Reserved4[0], pp_cap->NotButton.Reserved4[1], pp_cap->NotButton.Reserved4[2]);
+ fprintf(file, "pp_data->cap[%d]->NotButton.LogicalMin = %ld\n", cap_idx, pp_cap->NotButton.LogicalMin);
+ fprintf(file, "pp_data->cap[%d]->NotButton.LogicalMax = %ld\n", cap_idx, pp_cap->NotButton.LogicalMax);
+ fprintf(file, "pp_data->cap[%d]->NotButton.PhysicalMin = %ld\n", cap_idx, pp_cap->NotButton.PhysicalMin);
+ fprintf(file, "pp_data->cap[%d]->NotButton.PhysicalMax = %ld\n", cap_idx, pp_cap->NotButton.PhysicalMax);
+ };
+ fprintf(file, "pp_data->cap[%d]->Units = %lu\n", cap_idx, pp_cap->Units);
+ fprintf(file, "pp_data->cap[%d]->UnitsExp = %lu\n", cap_idx, pp_cap->UnitsExp);
+}
+
+void dump_hidp_link_collection_node(FILE* file, phid_pp_link_collection_node pcoll, unsigned int coll_idx) {
+ fprintf(file, "pp_data->LinkCollectionArray[%d]->LinkUsage = 0x%04hX\n", coll_idx, pcoll->LinkUsage);
+ fprintf(file, "pp_data->LinkCollectionArray[%d]->LinkUsagePage = 0x%04hX\n", coll_idx, pcoll->LinkUsagePage);
+ fprintf(file, "pp_data->LinkCollectionArray[%d]->Parent = %hu\n", coll_idx, pcoll->Parent);
+ fprintf(file, "pp_data->LinkCollectionArray[%d]->NumberOfChildren = %hu\n", coll_idx, pcoll->NumberOfChildren);
+ fprintf(file, "pp_data->LinkCollectionArray[%d]->NextSibling = %hu\n", coll_idx, pcoll->NextSibling);
+ fprintf(file, "pp_data->LinkCollectionArray[%d]->FirstChild = %hu\n", coll_idx, pcoll->FirstChild);
+ fprintf(file, "pp_data->LinkCollectionArray[%d]->CollectionType = %d\n", coll_idx, pcoll->CollectionType);
+ fprintf(file, "pp_data->LinkCollectionArray[%d]->IsAlias = %d\n", coll_idx, pcoll->IsAlias);
+ fprintf(file, "pp_data->LinkCollectionArray[%d]->Reserved = 0x%08X\n", coll_idx, pcoll->Reserved);
+}
+
+int dump_pp_data(FILE* file, hid_device* dev)
+{
+ BOOL res;
+ hidp_preparsed_data* pp_data = NULL;
+
+ res = HidD_GetPreparsedData(dev->device_handle, (PHIDP_PREPARSED_DATA*) &pp_data);
+ if (!res) {
+ printf("ERROR: HidD_GetPreparsedData failed!");
+ return -1;
+ }
+ else {
+ fprintf(file, "pp_data->MagicKey = 0x%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX\n", pp_data->MagicKey[0], pp_data->MagicKey[1], pp_data->MagicKey[2], pp_data->MagicKey[3], pp_data->MagicKey[4], pp_data->MagicKey[5], pp_data->MagicKey[6], pp_data->MagicKey[7]);
+ fprintf(file, "pp_data->Usage = 0x%04hX\n", pp_data->Usage);
+ fprintf(file, "pp_data->UsagePage = 0x%04hX\n", pp_data->UsagePage);
+ fprintf(file, "pp_data->Reserved = 0x%04hX%04hX\n", pp_data->Reserved[0], pp_data->Reserved[1]);
+ fprintf(file, "# Input caps_info struct:\n");
+ fprintf(file, "pp_data->caps_info[0]->FirstCap = %hu\n", pp_data->caps_info[0].FirstCap);
+ fprintf(file, "pp_data->caps_info[0]->LastCap = %hu\n", pp_data->caps_info[0].LastCap);
+ fprintf(file, "pp_data->caps_info[0]->NumberOfCaps = %hu\n", pp_data->caps_info[0].NumberOfCaps);
+ fprintf(file, "pp_data->caps_info[0]->ReportByteLength = %hu\n", pp_data->caps_info[0].ReportByteLength);
+ fprintf(file, "# Output caps_info struct:\n");
+ fprintf(file, "pp_data->caps_info[1]->FirstCap = %hu\n", pp_data->caps_info[1].FirstCap);
+ fprintf(file, "pp_data->caps_info[1]->LastCap = %hu\n", pp_data->caps_info[1].LastCap);
+ fprintf(file, "pp_data->caps_info[1]->NumberOfCaps = %hu\n", pp_data->caps_info[1].NumberOfCaps);
+ fprintf(file, "pp_data->caps_info[1]->ReportByteLength = %hu\n", pp_data->caps_info[1].ReportByteLength);
+ fprintf(file, "# Feature caps_info struct:\n");
+ fprintf(file, "pp_data->caps_info[2]->FirstCap = %hu\n", pp_data->caps_info[2].FirstCap);
+ fprintf(file, "pp_data->caps_info[2]->LastCap = %hu\n", pp_data->caps_info[2].LastCap);
+ fprintf(file, "pp_data->caps_info[2]->NumberOfCaps = %hu\n", pp_data->caps_info[2].NumberOfCaps);
+ fprintf(file, "pp_data->caps_info[2]->ReportByteLength = %hu\n", pp_data->caps_info[2].ReportByteLength);
+ fprintf(file, "# LinkCollectionArray Offset & Size:\n");
+ fprintf(file, "pp_data->FirstByteOfLinkCollectionArray = 0x%04hX\n", pp_data->FirstByteOfLinkCollectionArray);
+ fprintf(file, "pp_data->NumberLinkCollectionNodes = %hu\n", pp_data->NumberLinkCollectionNodes);
+
+
+ phid_pp_cap pcap = (phid_pp_cap)(((unsigned char*)pp_data) + offsetof(hidp_preparsed_data, caps));
+ fprintf(file, "# Input hid_pp_cap struct:\n");
+ for (int caps_idx = pp_data->caps_info[0].FirstCap; caps_idx < pp_data->caps_info[0].LastCap; caps_idx++) {
+ dump_hid_pp_cap(file, pcap + caps_idx, caps_idx);
+ fprintf(file, "\n");
+ }
+ fprintf(file, "# Output hid_pp_cap struct:\n");
+ for (int caps_idx = pp_data->caps_info[1].FirstCap; caps_idx < pp_data->caps_info[1].LastCap; caps_idx++) {
+ dump_hid_pp_cap(file, pcap + caps_idx, caps_idx);
+ fprintf(file, "\n");
+ }
+ fprintf(file, "# Feature hid_pp_cap struct:\n");
+ for (int caps_idx = pp_data->caps_info[2].FirstCap; caps_idx < pp_data->caps_info[2].LastCap; caps_idx++) {
+ dump_hid_pp_cap(file, pcap + caps_idx, caps_idx);
+ fprintf(file, "\n");
+ }
+
+ phid_pp_link_collection_node pcoll = (phid_pp_link_collection_node)(((unsigned char*)pcap) + pp_data->FirstByteOfLinkCollectionArray);
+ fprintf(file, "# Link Collections:\n");
+ for (int coll_idx = 0; coll_idx < pp_data->NumberLinkCollectionNodes; coll_idx++) {
+ dump_hidp_link_collection_node(file, pcoll + coll_idx, coll_idx);
+ }
+
+ HidD_FreePreparsedData((PHIDP_PREPARSED_DATA) pp_data);
+ return 0;
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ (void)argc;
+ (void)argv;
+
+ #define MAX_STR 255
+
+ struct hid_device_info *devs, *cur_dev;
+
+ printf("pp_data_dump tool. Compiled with hidapi version %s, runtime version %s.\n", HID_API_VERSION_STR, hid_version_str());
+ if (hid_version()->major == HID_API_VERSION_MAJOR && hid_version()->minor == HID_API_VERSION_MINOR && hid_version()->patch == HID_API_VERSION_PATCH) {
+ printf("Compile-time version matches runtime version of hidapi.\n\n");
+ }
+ else {
+ printf("Compile-time version is different than runtime version of hidapi.\n]n");
+ }
+
+ if (hid_init())
+ return -1;
+
+ devs = hid_enumerate(0x0, 0x0);
+ cur_dev = devs;
+ while (cur_dev) {
+ printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
+ printf("\n");
+ printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string);
+ printf(" Product: %ls\n", cur_dev->product_string);
+ printf(" Release: %hX\n", cur_dev->release_number);
+ printf(" Interface: %d\n", cur_dev->interface_number);
+ printf(" Usage (page): %02X (%02X)\n", cur_dev->usage, cur_dev->usage_page);
+
+ hid_device *device = hid_open_path(cur_dev->path);
+ if (device) {
+ char filename[MAX_STR];
+ FILE* file;
+
+ sprintf_s(filename, MAX_STR, "%04X_%04X_%04X_%04X.pp_data", cur_dev->vendor_id, cur_dev->product_id, cur_dev->usage, cur_dev->usage_page);
+ errno_t err = fopen_s(&file, filename, "w");
+ if (err == 0) {
+ fprintf(file, "# HIDAPI device info struct:\n");
+ fprintf(file, "dev->vendor_id = 0x%04hX\n", cur_dev->vendor_id);
+ fprintf(file, "dev->product_id = 0x%04hX\n", cur_dev->product_id);
+ fprintf(file, "dev->manufacturer_string = \"%ls\"\n", cur_dev->manufacturer_string);
+ fprintf(file, "dev->product_string = \"%ls\"\n", cur_dev->product_string);
+ fprintf(file, "dev->release_number = 0x%04hX\n", cur_dev->release_number);
+ fprintf(file, "dev->interface_number = %d\n", cur_dev->interface_number);
+ fprintf(file, "dev->usage = 0x%04hX\n", cur_dev->usage);
+ fprintf(file, "dev->usage_page = 0x%04hX\n", cur_dev->usage_page);
+ fprintf(file, "dev->path = \"%s\"\n", cur_dev->path);
+ fprintf(file, "\n# Preparsed Data struct:\n");
+ int res = dump_pp_data(file, device);
+
+ if (res == 0) {
+ printf("Dumped Preparsed Data to %s\n", filename);
+ }
+ else {
+ printf("ERROR: Dump Preparsed Data to %s failed!\n", filename);
+ }
+
+ fclose(file);
+ }
+
+ hid_close(device);
+ }
+ else {
+ printf(" Device: not available.\n");
+ }
+
+ printf("\n");
+ cur_dev = cur_dev->next;
+ }
+ hid_free_enumeration(devs);
+
+
+ /* Free static HIDAPI objects. */
+ hid_exit();
+
+ //system("pause");
+
+ return 0;
+}
diff --git a/src/hidapi/windows/test/CMakeLists.txt b/src/hidapi/windows/test/CMakeLists.txt
new file mode 100644
index 0000000..eae3217
--- /dev/null
+++ b/src/hidapi/windows/test/CMakeLists.txt
@@ -0,0 +1,76 @@
+add_executable(hid_report_reconstructor_test hid_report_reconstructor_test.c)
+set_target_properties(hid_report_reconstructor_test
+ PROPERTIES
+ C_STANDARD 11
+ C_STANDARD_REQUIRED TRUE
+)
+
+target_link_libraries(hid_report_reconstructor_test
+ PRIVATE hidapi_include hidapi_winapi
+)
+
+# Each test case requires 2 files:
+# <name>.pp_data - textual representation of HIDP_PREPARSED_DATA;
+# <name>_expected.rpt_desc - reconstructed HID Report Descriptor out of .pp_data file;
+#
+# (Non-required by test):
+# <name>_real.dpt_desc - the original report rescriptor used to create a test case;
+set(HID_DESCRIPTOR_RECONSTRUCT_TEST_CASES
+ 046D_C52F_0001_000C
+ 046D_C52F_0001_FF00
+ 046D_C52F_0002_0001
+ 046D_C52F_0002_FF00
+ 17CC_1130_0000_FF01
+ 046D_0A37_0001_000C
+ 046A_0011_0006_0001
+ 046D_C077_0002_0001
+ 046D_C283_0004_0001
+ 046D_B010_0006_0001
+ 046D_B010_0002_FF00
+ 046D_B010_0002_0001
+ 046D_B010_0001_FF00
+ 046D_B010_0001_000C
+ 046D_C534_0001_000C
+ 046D_C534_0001_FF00
+ 046D_C534_0002_0001
+ 046D_C534_0002_FF00
+ 046D_C534_0006_0001
+ 046D_C534_0080_0001
+ 047F_C056_0001_000C
+ 047F_C056_0003_FFA0
+ 047F_C056_0005_000B
+ 045E_02FF_0005_0001
+)
+
+set(CMAKE_VERSION_SUPPORTS_ENVIRONMENT_MODIFICATION "3.22")
+
+if(HIDAPI_ENABLE_ASAN AND MSVC)
+ if(CMAKE_VERSION VERSION_LESS CMAKE_VERSION_SUPPORTS_ENVIRONMENT_MODIFICATION)
+ message("CTest/ASAN: Make sure to run ctest from MSVC Command Prompt")
+ endif()
+endif()
+
+foreach(TEST_CASE ${HID_DESCRIPTOR_RECONSTRUCT_TEST_CASES})
+ set(TEST_PP_DATA "${CMAKE_CURRENT_LIST_DIR}/data/${TEST_CASE}.pp_data")
+ if(NOT EXISTS "${TEST_PP_DATA}")
+ message(FATAL_ERROR "Missing '${TEST_PP_DATA}' file for '${TEST_CASE}' test case")
+ endif()
+ set(TEST_EXPECTED_DESCRIPTOR "${CMAKE_CURRENT_LIST_DIR}/data/${TEST_CASE}_expected.rpt_desc")
+ if(NOT EXISTS "${TEST_EXPECTED_DESCRIPTOR}")
+ message(FATAL_ERROR "Missing '${TEST_EXPECTED_DESCRIPTOR}' file for '${TEST_CASE}' test case")
+ endif()
+
+ add_test(NAME "WinHidReportReconstructTest_${TEST_CASE}"
+ COMMAND hid_report_reconstructor_test "${TEST_PP_DATA}" "${TEST_EXPECTED_DESCRIPTOR}"
+ WORKING_DIRECTORY "$<TARGET_FILE_DIR:hidapi_winapi>"
+ )
+ if(HIDAPI_ENABLE_ASAN)
+ if(MSVC)
+ if(NOT CMAKE_VERSION VERSION_LESS CMAKE_VERSION_SUPPORTS_ENVIRONMENT_MODIFICATION)
+ get_filename_component(MSVC_BUILD_TOOLS_DIR "${CMAKE_LINKER}" DIRECTORY)
+ set_property(TEST "WinHidReportReconstructTest_${TEST_CASE}" PROPERTY ENVIRONMENT_MODIFICATION "PATH=path_list_append:${MSVC_BUILD_TOOLS_DIR}")
+ endif()
+ endif()
+ set_property(TEST "WinHidReportReconstructTest_${TEST_CASE}" PROPERTY ENVIRONMENT "ASAN_SAVE_DUMPS=AsanDump_${TEST_CASE}.dmp")
+ endif()
+endforeach()
diff --git a/src/hidapi/windows/test/data/045E_02FF_0005_0001.pp_data b/src/hidapi/windows/test/data/045E_02FF_0005_0001.pp_data
new file mode 100644
index 0000000..6226996
--- /dev/null
+++ b/src/hidapi/windows/test/data/045E_02FF_0005_0001.pp_data
@@ -0,0 +1,420 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x045E
+dev->product_id = 0x02FF
+dev->manufacturer_string = ""
+dev->product_string = "Controller (Xbox One For Windows)"
+dev->release_number = 0x0000
+dev->interface_number = -1
+dev->usage = 0x0005
+dev->usage_page = 0x0001
+dev->path = "\\?\HID#VID_045E&PID_02FF&IG_00#7&5ea4a81&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0005
+pp_data->UsagePage = 0x0001
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 7
+pp_data->caps_info[0]->NumberOfCaps = 7
+pp_data->caps_info[0]->ReportByteLength = 16
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 7
+pp_data->caps_info[1]->LastCap = 7
+pp_data->caps_info[1]->NumberOfCaps = 0
+pp_data->caps_info[1]->ReportByteLength = 0
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 7
+pp_data->caps_info[2]->LastCap = 7
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x02D8
+pp_data->NumberLinkCollectionNodes = 4
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x0001
+pp_data->cap[0]->ReportID = 0x00
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 16
+pp_data->cap[0]->ReportCount = 1
+pp_data->cap[0]->BytePosition = 0x0003
+pp_data->cap[0]->BitCount = 16
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0005
+pp_data->cap[0]->LinkCollection = 0x0001
+pp_data->cap[0]->LinkUsagePage = 0x0001
+pp_data->cap[0]->LinkUsage = 0x0000
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 0
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0031
+pp_data->cap[0]->NotRange.Reserved1 = 0x0031
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->NotButton.HasNull = 0
+pp_data->cap[0]->NotButton.Reserved4 = 0x000000
+pp_data->cap[0]->NotButton.LogicalMin = 0
+pp_data->cap[0]->NotButton.LogicalMax = -1
+pp_data->cap[0]->NotButton.PhysicalMin = 0
+pp_data->cap[0]->NotButton.PhysicalMax = -1
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x0001
+pp_data->cap[1]->ReportID = 0x00
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 16
+pp_data->cap[1]->ReportCount = 1
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 16
+pp_data->cap[1]->BitField = 0x02
+pp_data->cap[1]->NextBytePosition = 0x0003
+pp_data->cap[1]->LinkCollection = 0x0001
+pp_data->cap[1]->LinkUsagePage = 0x0001
+pp_data->cap[1]->LinkUsage = 0x0000
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 0
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0030
+pp_data->cap[1]->NotRange.Reserved1 = 0x0030
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 1
+pp_data->cap[1]->NotRange.Reserved4 = 1
+pp_data->cap[1]->NotButton.HasNull = 0
+pp_data->cap[1]->NotButton.Reserved4 = 0x000000
+pp_data->cap[1]->NotButton.LogicalMin = 0
+pp_data->cap[1]->NotButton.LogicalMax = -1
+pp_data->cap[1]->NotButton.PhysicalMin = 0
+pp_data->cap[1]->NotButton.PhysicalMax = -1
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+pp_data->cap[2]->UsagePage = 0x0001
+pp_data->cap[2]->ReportID = 0x00
+pp_data->cap[2]->BitPosition = 0
+pp_data->cap[2]->BitSize = 16
+pp_data->cap[2]->ReportCount = 1
+pp_data->cap[2]->BytePosition = 0x0007
+pp_data->cap[2]->BitCount = 16
+pp_data->cap[2]->BitField = 0x02
+pp_data->cap[2]->NextBytePosition = 0x0009
+pp_data->cap[2]->LinkCollection = 0x0002
+pp_data->cap[2]->LinkUsagePage = 0x0001
+pp_data->cap[2]->LinkUsage = 0x0000
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 0
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 1
+pp_data->cap[2]->IsRange = 0
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->NotRange.Usage = 0x0034
+pp_data->cap[2]->NotRange.Reserved1 = 0x0034
+pp_data->cap[2]->NotRange.StringIndex = 0
+pp_data->cap[2]->NotRange.Reserved2 = 0
+pp_data->cap[2]->NotRange.DesignatorIndex = 0
+pp_data->cap[2]->NotRange.Reserved3 = 0
+pp_data->cap[2]->NotRange.DataIndex = 2
+pp_data->cap[2]->NotRange.Reserved4 = 2
+pp_data->cap[2]->NotButton.HasNull = 0
+pp_data->cap[2]->NotButton.Reserved4 = 0x000000
+pp_data->cap[2]->NotButton.LogicalMin = 0
+pp_data->cap[2]->NotButton.LogicalMax = -1
+pp_data->cap[2]->NotButton.PhysicalMin = 0
+pp_data->cap[2]->NotButton.PhysicalMax = -1
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+pp_data->cap[3]->UsagePage = 0x0001
+pp_data->cap[3]->ReportID = 0x00
+pp_data->cap[3]->BitPosition = 0
+pp_data->cap[3]->BitSize = 16
+pp_data->cap[3]->ReportCount = 1
+pp_data->cap[3]->BytePosition = 0x0005
+pp_data->cap[3]->BitCount = 16
+pp_data->cap[3]->BitField = 0x02
+pp_data->cap[3]->NextBytePosition = 0x0007
+pp_data->cap[3]->LinkCollection = 0x0002
+pp_data->cap[3]->LinkUsagePage = 0x0001
+pp_data->cap[3]->LinkUsage = 0x0000
+pp_data->cap[3]->IsMultipleItemsForArray = 0
+pp_data->cap[3]->IsButtonCap = 0
+pp_data->cap[3]->IsPadding = 0
+pp_data->cap[3]->IsAbsolute = 1
+pp_data->cap[3]->IsRange = 0
+pp_data->cap[3]->IsAlias = 0
+pp_data->cap[3]->IsStringRange = 0
+pp_data->cap[3]->IsDesignatorRange = 0
+pp_data->cap[3]->Reserved1 = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[3]->NotRange.Usage = 0x0033
+pp_data->cap[3]->NotRange.Reserved1 = 0x0033
+pp_data->cap[3]->NotRange.StringIndex = 0
+pp_data->cap[3]->NotRange.Reserved2 = 0
+pp_data->cap[3]->NotRange.DesignatorIndex = 0
+pp_data->cap[3]->NotRange.Reserved3 = 0
+pp_data->cap[3]->NotRange.DataIndex = 3
+pp_data->cap[3]->NotRange.Reserved4 = 3
+pp_data->cap[3]->NotButton.HasNull = 0
+pp_data->cap[3]->NotButton.Reserved4 = 0x000000
+pp_data->cap[3]->NotButton.LogicalMin = 0
+pp_data->cap[3]->NotButton.LogicalMax = -1
+pp_data->cap[3]->NotButton.PhysicalMin = 0
+pp_data->cap[3]->NotButton.PhysicalMax = -1
+pp_data->cap[3]->Units = 0
+pp_data->cap[3]->UnitsExp = 0
+
+pp_data->cap[4]->UsagePage = 0x0001
+pp_data->cap[4]->ReportID = 0x00
+pp_data->cap[4]->BitPosition = 0
+pp_data->cap[4]->BitSize = 16
+pp_data->cap[4]->ReportCount = 1
+pp_data->cap[4]->BytePosition = 0x0009
+pp_data->cap[4]->BitCount = 16
+pp_data->cap[4]->BitField = 0x02
+pp_data->cap[4]->NextBytePosition = 0x000B
+pp_data->cap[4]->LinkCollection = 0x0003
+pp_data->cap[4]->LinkUsagePage = 0x0001
+pp_data->cap[4]->LinkUsage = 0x0000
+pp_data->cap[4]->IsMultipleItemsForArray = 0
+pp_data->cap[4]->IsButtonCap = 0
+pp_data->cap[4]->IsPadding = 0
+pp_data->cap[4]->IsAbsolute = 1
+pp_data->cap[4]->IsRange = 0
+pp_data->cap[4]->IsAlias = 0
+pp_data->cap[4]->IsStringRange = 0
+pp_data->cap[4]->IsDesignatorRange = 0
+pp_data->cap[4]->Reserved1 = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[4]->NotRange.Usage = 0x0032
+pp_data->cap[4]->NotRange.Reserved1 = 0x0032
+pp_data->cap[4]->NotRange.StringIndex = 0
+pp_data->cap[4]->NotRange.Reserved2 = 0
+pp_data->cap[4]->NotRange.DesignatorIndex = 0
+pp_data->cap[4]->NotRange.Reserved3 = 0
+pp_data->cap[4]->NotRange.DataIndex = 4
+pp_data->cap[4]->NotRange.Reserved4 = 4
+pp_data->cap[4]->NotButton.HasNull = 0
+pp_data->cap[4]->NotButton.Reserved4 = 0x000000
+pp_data->cap[4]->NotButton.LogicalMin = 0
+pp_data->cap[4]->NotButton.LogicalMax = -1
+pp_data->cap[4]->NotButton.PhysicalMin = 0
+pp_data->cap[4]->NotButton.PhysicalMax = -1
+pp_data->cap[4]->Units = 0
+pp_data->cap[4]->UnitsExp = 0
+
+pp_data->cap[5]->UsagePage = 0x0009
+pp_data->cap[5]->ReportID = 0x00
+pp_data->cap[5]->BitPosition = 0
+pp_data->cap[5]->BitSize = 1
+pp_data->cap[5]->ReportCount = 16
+pp_data->cap[5]->BytePosition = 0x000B
+pp_data->cap[5]->BitCount = 16
+pp_data->cap[5]->BitField = 0x02
+pp_data->cap[5]->NextBytePosition = 0x000D
+pp_data->cap[5]->LinkCollection = 0x0000
+pp_data->cap[5]->LinkUsagePage = 0x0001
+pp_data->cap[5]->LinkUsage = 0x0005
+pp_data->cap[5]->IsMultipleItemsForArray = 0
+pp_data->cap[5]->IsButtonCap = 1
+pp_data->cap[5]->IsPadding = 0
+pp_data->cap[5]->IsAbsolute = 1
+pp_data->cap[5]->IsRange = 1
+pp_data->cap[5]->IsAlias = 0
+pp_data->cap[5]->IsStringRange = 0
+pp_data->cap[5]->IsDesignatorRange = 0
+pp_data->cap[5]->Reserved1 = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[5]->Range.UsageMin = 0x0001
+pp_data->cap[5]->Range.UsageMax = 0x0010
+pp_data->cap[5]->Range.StringMin = 0
+pp_data->cap[5]->Range.StringMax = 0
+pp_data->cap[5]->Range.DesignatorMin = 0
+pp_data->cap[5]->Range.DesignatorMax = 0
+pp_data->cap[5]->Range.DataIndexMin = 5
+pp_data->cap[5]->Range.DataIndexMax = 20
+pp_data->cap[5]->Button.LogicalMin = 0
+pp_data->cap[5]->Button.LogicalMax = 0
+pp_data->cap[5]->Units = 0
+pp_data->cap[5]->UnitsExp = 0
+
+pp_data->cap[6]->UsagePage = 0x0001
+pp_data->cap[6]->ReportID = 0x00
+pp_data->cap[6]->BitPosition = 0
+pp_data->cap[6]->BitSize = 4
+pp_data->cap[6]->ReportCount = 1
+pp_data->cap[6]->BytePosition = 0x000D
+pp_data->cap[6]->BitCount = 4
+pp_data->cap[6]->BitField = 0x42
+pp_data->cap[6]->NextBytePosition = 0x000E
+pp_data->cap[6]->LinkCollection = 0x0000
+pp_data->cap[6]->LinkUsagePage = 0x0001
+pp_data->cap[6]->LinkUsage = 0x0005
+pp_data->cap[6]->IsMultipleItemsForArray = 0
+pp_data->cap[6]->IsButtonCap = 0
+pp_data->cap[6]->IsPadding = 0
+pp_data->cap[6]->IsAbsolute = 1
+pp_data->cap[6]->IsRange = 0
+pp_data->cap[6]->IsAlias = 0
+pp_data->cap[6]->IsStringRange = 0
+pp_data->cap[6]->IsDesignatorRange = 0
+pp_data->cap[6]->Reserved1 = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[6]->NotRange.Usage = 0x0039
+pp_data->cap[6]->NotRange.Reserved1 = 0x0039
+pp_data->cap[6]->NotRange.StringIndex = 0
+pp_data->cap[6]->NotRange.Reserved2 = 0
+pp_data->cap[6]->NotRange.DesignatorIndex = 0
+pp_data->cap[6]->NotRange.Reserved3 = 0
+pp_data->cap[6]->NotRange.DataIndex = 21
+pp_data->cap[6]->NotRange.Reserved4 = 21
+pp_data->cap[6]->NotButton.HasNull = 1
+pp_data->cap[6]->NotButton.Reserved4 = 0x000000
+pp_data->cap[6]->NotButton.LogicalMin = 1
+pp_data->cap[6]->NotButton.LogicalMax = 8
+pp_data->cap[6]->NotButton.PhysicalMin = 0
+pp_data->cap[6]->NotButton.PhysicalMax = 4155
+pp_data->cap[6]->Units = 14
+pp_data->cap[6]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0005
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 3
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 3
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[1]->LinkUsage = 0x0000
+pp_data->LinkCollectionArray[1]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[1]->Parent = 0
+pp_data->LinkCollectionArray[1]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[1]->NextSibling = 0
+pp_data->LinkCollectionArray[1]->FirstChild = 0
+pp_data->LinkCollectionArray[1]->CollectionType = 0
+pp_data->LinkCollectionArray[1]->IsAlias = 0
+pp_data->LinkCollectionArray[1]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[2]->LinkUsage = 0x0000
+pp_data->LinkCollectionArray[2]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[2]->Parent = 0
+pp_data->LinkCollectionArray[2]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[2]->NextSibling = 1
+pp_data->LinkCollectionArray[2]->FirstChild = 0
+pp_data->LinkCollectionArray[2]->CollectionType = 0
+pp_data->LinkCollectionArray[2]->IsAlias = 0
+pp_data->LinkCollectionArray[2]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[3]->LinkUsage = 0x0000
+pp_data->LinkCollectionArray[3]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[3]->Parent = 0
+pp_data->LinkCollectionArray[3]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[3]->NextSibling = 2
+pp_data->LinkCollectionArray[3]->FirstChild = 0
+pp_data->LinkCollectionArray[3]->CollectionType = 0
+pp_data->LinkCollectionArray[3]->IsAlias = 0
+pp_data->LinkCollectionArray[3]->Reserved = 0x00000000
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/045E_02FF_0005_0001_expected.rpt_desc b/src/hidapi/windows/test/data/045E_02FF_0005_0001_expected.rpt_desc
new file mode 100644
index 0000000..58f80e4
--- /dev/null
+++ b/src/hidapi/windows/test/data/045E_02FF_0005_0001_expected.rpt_desc
@@ -0,0 +1,12 @@
+0x05, 0x01, 0x09, 0x05, 0xA1, 0x01, 0x09, 0x00, 0xA1, 0x00,
+0x09, 0x30, 0x09, 0x31, 0x15, 0x00, 0x25, 0xFF, 0x35, 0x00,
+0x45, 0xFF, 0x75, 0x10, 0x95, 0x02, 0x81, 0x02, 0xC0, 0x09,
+0x00, 0xA1, 0x00, 0x09, 0x33, 0x09, 0x34, 0x15, 0x00, 0x25,
+0xFF, 0x75, 0x10, 0x95, 0x02, 0x81, 0x02, 0xC0, 0x09, 0x00,
+0xA1, 0x00, 0x09, 0x32, 0x15, 0x00, 0x25, 0xFF, 0x75, 0x10,
+0x95, 0x01, 0x81, 0x02, 0xC0, 0x05, 0x09, 0x19, 0x01, 0x29,
+0x10, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x10, 0x45,
+0x00, 0x81, 0x02, 0x05, 0x01, 0x09, 0x39, 0x15, 0x01, 0x25,
+0x08, 0x35, 0x00, 0x46, 0x3B, 0x10, 0x65, 0x0E, 0x75, 0x04,
+0x95, 0x01, 0x81, 0x42, 0x75, 0x04, 0x95, 0x01, 0x81, 0x03,
+0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/045E_02FF_0005_0001_real.rpt_desc b/src/hidapi/windows/test/data/045E_02FF_0005_0001_real.rpt_desc
new file mode 100644
index 0000000..11cc78b
--- /dev/null
+++ b/src/hidapi/windows/test/data/045E_02FF_0005_0001_real.rpt_desc
@@ -0,0 +1,64 @@
+// Special cases of this device:
+// 2 full padding bytes at the end
+// Multiple child collections inside of the same report (byte position of Input items defines collection order)
+
+0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+0x09, 0x05, // Usage (Game Pad)
+0xA1, 0x01, // Collection (Application)
+0xA1, 0x00, // Collection (Physical)
+0x09, 0x30, // Usage (X)
+0x09, 0x31, // Usage (Y)
+0x15, 0x00, // Logical Minimum (0)
+0x26, 0xFF, 0xFF, // Logical Maximum (-1)
+0x35, 0x00, // Physical Minimum (0)
+0x46, 0xFF, 0xFF, // Physical Maximum (-1)
+0x95, 0x02, // Report Count (2)
+0x75, 0x10, // Report Size (16)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
+0xA1, 0x00, // Collection (Physical)
+0x09, 0x33, // Usage (Rx)
+0x09, 0x34, // Usage (Ry)
+0x15, 0x00, // Logical Minimum (0)
+0x26, 0xFF, 0xFF, // Logical Maximum (-1)
+0x35, 0x00, // Physical Minimum (0)
+0x46, 0xFF, 0xFF, // Physical Maximum (-1)
+0x95, 0x02, // Report Count (2)
+0x75, 0x10, // Report Size (16)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
+0xA1, 0x00, // Collection (Physical)
+0x09, 0x32, // Usage (Z)
+0x15, 0x00, // Logical Minimum (0)
+0x26, 0xFF, 0xFF, // Logical Maximum (-1)
+0x35, 0x00, // Physical Minimum (0)
+0x46, 0xFF, 0xFF, // Physical Maximum (-1)
+0x95, 0x01, // Report Count (1)
+0x75, 0x10, // Report Size (16)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
+0x05, 0x09, // Usage Page (Button)
+0x19, 0x01, // Usage Minimum (0x01)
+0x29, 0x10, // Usage Maximum (0x10)
+0x95, 0x10, // Report Count (16)
+0x75, 0x01, // Report Size (1)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+0x09, 0x39, // Usage (Hat switch)
+0x15, 0x01, // Logical Minimum (1)
+0x25, 0x08, // Logical Maximum (8)
+0x35, 0x00, // Physical Minimum (0)
+0x46, 0x3B, 0x10, // Physical Maximum (4155)
+0x66, 0x0E, 0x00, // Unit (None)
+0x75, 0x04, // Report Size (4)
+0x95, 0x01, // Report Count (1)
+0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
+0x75, 0x04, // Report Size (4)
+0x95, 0x01, // Report Count (1)
+0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x75, 0x08, // Report Size (8)
+0x95, 0x02, // Report Count (2)
+0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
+
+// 120 bytes
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046A_0011_0006_0001.pp_data b/src/hidapi/windows/test/data/046A_0011_0006_0001.pp_data
new file mode 100644
index 0000000..59eb600
--- /dev/null
+++ b/src/hidapi/windows/test/data/046A_0011_0006_0001.pp_data
@@ -0,0 +1,183 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046A
+dev->product_id = 0x0011
+dev->manufacturer_string = "dev->product_string = "dev->release_number = 0x0100
+dev->interface_number = -1
+dev->usage = 0x0006
+dev->usage_page = 0x0001
+dev->path = "\\?\hid#vid_046a&pid_0011#7&2c7fd0a5&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0006
+pp_data->UsagePage = 0x0001
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 2
+pp_data->caps_info[0]->NumberOfCaps = 2
+pp_data->caps_info[0]->ReportByteLength = 9
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 2
+pp_data->caps_info[1]->LastCap = 3
+pp_data->caps_info[1]->NumberOfCaps = 1
+pp_data->caps_info[1]->ReportByteLength = 2
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 3
+pp_data->caps_info[2]->LastCap = 3
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x0138
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x0007
+pp_data->cap[0]->ReportID = 0x00
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 1
+pp_data->cap[0]->ReportCount = 8
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 8
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0002
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0x0001
+pp_data->cap[0]->LinkUsage = 0x0006
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 1
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->Range.UsageMin = 0x00E0
+pp_data->cap[0]->Range.UsageMax = 0x00E7
+pp_data->cap[0]->Range.StringMin = 0
+pp_data->cap[0]->Range.StringMax = 0
+pp_data->cap[0]->Range.DesignatorMin = 0
+pp_data->cap[0]->Range.DesignatorMax = 0
+pp_data->cap[0]->Range.DataIndexMin = 0
+pp_data->cap[0]->Range.DataIndexMax = 7
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x0007
+pp_data->cap[1]->ReportID = 0x00
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 8
+pp_data->cap[1]->ReportCount = 6
+pp_data->cap[1]->BytePosition = 0x0003
+pp_data->cap[1]->BitCount = 48
+pp_data->cap[1]->BitField = 0x00
+pp_data->cap[1]->NextBytePosition = 0x0009
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0x0001
+pp_data->cap[1]->LinkUsage = 0x0006
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 1
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->Range.UsageMin = 0x0000
+pp_data->cap[1]->Range.UsageMax = 0x00DD
+pp_data->cap[1]->Range.StringMin = 0
+pp_data->cap[1]->Range.StringMax = 0
+pp_data->cap[1]->Range.DesignatorMin = 0
+pp_data->cap[1]->Range.DesignatorMax = 0
+pp_data->cap[1]->Range.DataIndexMin = 8
+pp_data->cap[1]->Range.DataIndexMax = 229
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 221
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[2]->UsagePage = 0x0008
+pp_data->cap[2]->ReportID = 0x00
+pp_data->cap[2]->BitPosition = 0
+pp_data->cap[2]->BitSize = 1
+pp_data->cap[2]->ReportCount = 3
+pp_data->cap[2]->BytePosition = 0x0001
+pp_data->cap[2]->BitCount = 3
+pp_data->cap[2]->BitField = 0x02
+pp_data->cap[2]->NextBytePosition = 0x0002
+pp_data->cap[2]->LinkCollection = 0x0000
+pp_data->cap[2]->LinkUsagePage = 0x0001
+pp_data->cap[2]->LinkUsage = 0x0006
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 1
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 1
+pp_data->cap[2]->IsRange = 1
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->Range.UsageMin = 0x0001
+pp_data->cap[2]->Range.UsageMax = 0x0003
+pp_data->cap[2]->Range.StringMin = 0
+pp_data->cap[2]->Range.StringMax = 0
+pp_data->cap[2]->Range.DesignatorMin = 0
+pp_data->cap[2]->Range.DesignatorMax = 0
+pp_data->cap[2]->Range.DataIndexMin = 0
+pp_data->cap[2]->Range.DataIndexMax = 2
+pp_data->cap[2]->Button.LogicalMin = 0
+pp_data->cap[2]->Button.LogicalMax = 0
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0006
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046A_0011_0006_0001_expected.rpt_desc b/src/hidapi/windows/test/data/046A_0011_0006_0001_expected.rpt_desc
new file mode 100644
index 0000000..e9bc501
--- /dev/null
+++ b/src/hidapi/windows/test/data/046A_0011_0006_0001_expected.rpt_desc
@@ -0,0 +1,7 @@
+0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0,
+0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08,
+0x81, 0x02, 0x75, 0x08, 0x95, 0x01, 0x81, 0x03, 0x19, 0x00,
+0x29, 0xDD, 0x15, 0x00, 0x26, 0xDD, 0x00, 0x75, 0x08, 0x95,
+0x06, 0x81, 0x00, 0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x15,
+0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x03, 0x91, 0x02, 0x75,
+0x05, 0x95, 0x01, 0x91, 0x03, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046A_0011_0006_0001_real.rpt_desc b/src/hidapi/windows/test/data/046A_0011_0006_0001_real.rpt_desc
new file mode 100644
index 0000000..e9bc501
--- /dev/null
+++ b/src/hidapi/windows/test/data/046A_0011_0006_0001_real.rpt_desc
@@ -0,0 +1,7 @@
+0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0,
+0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08,
+0x81, 0x02, 0x75, 0x08, 0x95, 0x01, 0x81, 0x03, 0x19, 0x00,
+0x29, 0xDD, 0x15, 0x00, 0x26, 0xDD, 0x00, 0x75, 0x08, 0x95,
+0x06, 0x81, 0x00, 0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x15,
+0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x03, 0x91, 0x02, 0x75,
+0x05, 0x95, 0x01, 0x91, 0x03, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_0A37_0001_000C.pp_data b/src/hidapi/windows/test/data/046D_0A37_0001_000C.pp_data
new file mode 100644
index 0000000..3bc7aad
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_0A37_0001_000C.pp_data
@@ -0,0 +1,532 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0x0A37
+dev->manufacturer_string = "Logitech Inc "
+dev->product_string = "Logitech USB Headset H540"
+dev->release_number = 0x0122
+dev->interface_number = 3
+dev->usage = 0x0001
+dev->usage_page = 0x000C
+dev->path = "\\?\hid#vid_046d&pid_0a37&mi_03#8&1717f300&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0001
+pp_data->UsagePage = 0x000C
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 7
+pp_data->caps_info[0]->NumberOfCaps = 9
+pp_data->caps_info[0]->ReportByteLength = 33
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 9
+pp_data->caps_info[1]->LastCap = 12
+pp_data->caps_info[1]->NumberOfCaps = 3
+pp_data->caps_info[1]->ReportByteLength = 37
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 12
+pp_data->caps_info[2]->LastCap = 12
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x04E0
+pp_data->NumberLinkCollectionNodes = 2
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x000C
+pp_data->cap[0]->ReportID = 0x01
+pp_data->cap[0]->BitPosition = 1
+pp_data->cap[0]->BitSize = 1
+pp_data->cap[0]->ReportCount = 1
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 1
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0002
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0x000C
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x00EA
+pp_data->cap[0]->NotRange.Reserved1 = 0x00EA
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x000C
+pp_data->cap[1]->ReportID = 0x01
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 1
+pp_data->cap[1]->ReportCount = 1
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 1
+pp_data->cap[1]->BitField = 0x02
+pp_data->cap[1]->NextBytePosition = 0x0002
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0x000C
+pp_data->cap[1]->LinkUsage = 0x0001
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x00E9
+pp_data->cap[1]->NotRange.Reserved1 = 0x00E9
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 1
+pp_data->cap[1]->NotRange.Reserved4 = 1
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 0
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+pp_data->cap[2]->UsagePage = 0x000C
+pp_data->cap[2]->ReportID = 0x01
+pp_data->cap[2]->BitPosition = 2
+pp_data->cap[2]->BitSize = 1
+pp_data->cap[2]->ReportCount = 1
+pp_data->cap[2]->BytePosition = 0x0001
+pp_data->cap[2]->BitCount = 1
+pp_data->cap[2]->BitField = 0x06
+pp_data->cap[2]->NextBytePosition = 0x0002
+pp_data->cap[2]->LinkCollection = 0x0000
+pp_data->cap[2]->LinkUsagePage = 0x000C
+pp_data->cap[2]->LinkUsage = 0x0001
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 1
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 0
+pp_data->cap[2]->IsRange = 0
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->NotRange.Usage = 0x00E2
+pp_data->cap[2]->NotRange.Reserved1 = 0x00E2
+pp_data->cap[2]->NotRange.StringIndex = 0
+pp_data->cap[2]->NotRange.Reserved2 = 0
+pp_data->cap[2]->NotRange.DesignatorIndex = 0
+pp_data->cap[2]->NotRange.Reserved3 = 0
+pp_data->cap[2]->NotRange.DataIndex = 2
+pp_data->cap[2]->NotRange.Reserved4 = 2
+pp_data->cap[2]->Button.LogicalMin = 0
+pp_data->cap[2]->Button.LogicalMax = 0
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+pp_data->cap[3]->UsagePage = 0x0009
+pp_data->cap[3]->ReportID = 0x01
+pp_data->cap[3]->BitPosition = 5
+pp_data->cap[3]->BitSize = 2
+pp_data->cap[3]->ReportCount = 1
+pp_data->cap[3]->BytePosition = 0x0001
+pp_data->cap[3]->BitCount = 2
+pp_data->cap[3]->BitField = 0x40
+pp_data->cap[3]->NextBytePosition = 0x0002
+pp_data->cap[3]->LinkCollection = 0x0001
+pp_data->cap[3]->LinkUsagePage = 0x000C
+pp_data->cap[3]->LinkUsage = 0x0036
+pp_data->cap[3]->IsMultipleItemsForArray = 0
+pp_data->cap[3]->IsButtonCap = 1
+pp_data->cap[3]->IsPadding = 0
+pp_data->cap[3]->IsAbsolute = 1
+pp_data->cap[3]->IsRange = 1
+pp_data->cap[3]->IsAlias = 0
+pp_data->cap[3]->IsStringRange = 0
+pp_data->cap[3]->IsDesignatorRange = 0
+pp_data->cap[3]->Reserved1 = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[3]->Range.UsageMin = 0x0001
+pp_data->cap[3]->Range.UsageMax = 0x0002
+pp_data->cap[3]->Range.StringMin = 0
+pp_data->cap[3]->Range.StringMax = 0
+pp_data->cap[3]->Range.DesignatorMin = 0
+pp_data->cap[3]->Range.DesignatorMax = 0
+pp_data->cap[3]->Range.DataIndexMin = 3
+pp_data->cap[3]->Range.DataIndexMax = 4
+pp_data->cap[3]->Button.LogicalMin = 1
+pp_data->cap[3]->Button.LogicalMax = 2
+pp_data->cap[3]->Units = 0
+pp_data->cap[3]->UnitsExp = 0
+
+pp_data->cap[4]->UsagePage = 0x000C
+pp_data->cap[4]->ReportID = 0x02
+pp_data->cap[4]->BitPosition = 0
+pp_data->cap[4]->BitSize = 1
+pp_data->cap[4]->ReportCount = 16
+pp_data->cap[4]->BytePosition = 0x0001
+pp_data->cap[4]->BitCount = 16
+pp_data->cap[4]->BitField = 0x02
+pp_data->cap[4]->NextBytePosition = 0x0003
+pp_data->cap[4]->LinkCollection = 0x0000
+pp_data->cap[4]->LinkUsagePage = 0x000C
+pp_data->cap[4]->LinkUsage = 0x0001
+pp_data->cap[4]->IsMultipleItemsForArray = 0
+pp_data->cap[4]->IsButtonCap = 1
+pp_data->cap[4]->IsPadding = 0
+pp_data->cap[4]->IsAbsolute = 1
+pp_data->cap[4]->IsRange = 0
+pp_data->cap[4]->IsAlias = 0
+pp_data->cap[4]->IsStringRange = 0
+pp_data->cap[4]->IsDesignatorRange = 0
+pp_data->cap[4]->Reserved1 = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[4]->NotRange.Usage = 0x0000
+pp_data->cap[4]->NotRange.Reserved1 = 0x0000
+pp_data->cap[4]->NotRange.StringIndex = 0
+pp_data->cap[4]->NotRange.Reserved2 = 0
+pp_data->cap[4]->NotRange.DesignatorIndex = 0
+pp_data->cap[4]->NotRange.Reserved3 = 0
+pp_data->cap[4]->NotRange.DataIndex = 5
+pp_data->cap[4]->NotRange.Reserved4 = 5
+pp_data->cap[4]->Button.LogicalMin = 0
+pp_data->cap[4]->Button.LogicalMax = 0
+pp_data->cap[4]->Units = 0
+pp_data->cap[4]->UnitsExp = 0
+
+pp_data->cap[5]->UsagePage = 0x000C
+pp_data->cap[5]->ReportID = 0x05
+pp_data->cap[5]->BitPosition = 0
+pp_data->cap[5]->BitSize = 8
+pp_data->cap[5]->ReportCount = 32
+pp_data->cap[5]->BytePosition = 0x0001
+pp_data->cap[5]->BitCount = 256
+pp_data->cap[5]->BitField = 0x02
+pp_data->cap[5]->NextBytePosition = 0x0021
+pp_data->cap[5]->LinkCollection = 0x0000
+pp_data->cap[5]->LinkUsagePage = 0x000C
+pp_data->cap[5]->LinkUsage = 0x0001
+pp_data->cap[5]->IsMultipleItemsForArray = 0
+pp_data->cap[5]->IsButtonCap = 0
+pp_data->cap[5]->IsPadding = 0
+pp_data->cap[5]->IsAbsolute = 1
+pp_data->cap[5]->IsRange = 0
+pp_data->cap[5]->IsAlias = 0
+pp_data->cap[5]->IsStringRange = 0
+pp_data->cap[5]->IsDesignatorRange = 0
+pp_data->cap[5]->Reserved1 = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[5]->NotRange.Usage = 0x0000
+pp_data->cap[5]->NotRange.Reserved1 = 0x0000
+pp_data->cap[5]->NotRange.StringIndex = 0
+pp_data->cap[5]->NotRange.Reserved2 = 0
+pp_data->cap[5]->NotRange.DesignatorIndex = 0
+pp_data->cap[5]->NotRange.Reserved3 = 0
+pp_data->cap[5]->NotRange.DataIndex = 6
+pp_data->cap[5]->NotRange.Reserved4 = 6
+pp_data->cap[5]->NotButton.HasNull = 0
+pp_data->cap[5]->NotButton.Reserved4 = 0x000000
+pp_data->cap[5]->NotButton.LogicalMin = 0
+pp_data->cap[5]->NotButton.LogicalMax = 1
+pp_data->cap[5]->NotButton.PhysicalMin = 0
+pp_data->cap[5]->NotButton.PhysicalMax = 0
+pp_data->cap[5]->Units = 0
+pp_data->cap[5]->UnitsExp = 0
+
+pp_data->cap[6]->UsagePage = 0x000C
+pp_data->cap[6]->ReportID = 0x07
+pp_data->cap[6]->BitPosition = 0
+pp_data->cap[6]->BitSize = 8
+pp_data->cap[6]->ReportCount = 32
+pp_data->cap[6]->BytePosition = 0x0001
+pp_data->cap[6]->BitCount = 256
+pp_data->cap[6]->BitField = 0x02
+pp_data->cap[6]->NextBytePosition = 0x0021
+pp_data->cap[6]->LinkCollection = 0x0000
+pp_data->cap[6]->LinkUsagePage = 0x000C
+pp_data->cap[6]->LinkUsage = 0x0001
+pp_data->cap[6]->IsMultipleItemsForArray = 0
+pp_data->cap[6]->IsButtonCap = 0
+pp_data->cap[6]->IsPadding = 0
+pp_data->cap[6]->IsAbsolute = 1
+pp_data->cap[6]->IsRange = 0
+pp_data->cap[6]->IsAlias = 0
+pp_data->cap[6]->IsStringRange = 0
+pp_data->cap[6]->IsDesignatorRange = 0
+pp_data->cap[6]->Reserved1 = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[6]->NotRange.Usage = 0x0000
+pp_data->cap[6]->NotRange.Reserved1 = 0x0000
+pp_data->cap[6]->NotRange.StringIndex = 0
+pp_data->cap[6]->NotRange.Reserved2 = 0
+pp_data->cap[6]->NotRange.DesignatorIndex = 0
+pp_data->cap[6]->NotRange.Reserved3 = 0
+pp_data->cap[6]->NotRange.DataIndex = 7
+pp_data->cap[6]->NotRange.Reserved4 = 7
+pp_data->cap[6]->NotButton.HasNull = 0
+pp_data->cap[6]->NotButton.Reserved4 = 0x000000
+pp_data->cap[6]->NotButton.LogicalMin = 0
+pp_data->cap[6]->NotButton.LogicalMax = 1
+pp_data->cap[6]->NotButton.PhysicalMin = 0
+pp_data->cap[6]->NotButton.PhysicalMax = 0
+pp_data->cap[6]->Units = 0
+pp_data->cap[6]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[9]->UsagePage = 0x000C
+pp_data->cap[9]->ReportID = 0x03
+pp_data->cap[9]->BitPosition = 0
+pp_data->cap[9]->BitSize = 1
+pp_data->cap[9]->ReportCount = 16
+pp_data->cap[9]->BytePosition = 0x0001
+pp_data->cap[9]->BitCount = 16
+pp_data->cap[9]->BitField = 0x02
+pp_data->cap[9]->NextBytePosition = 0x0003
+pp_data->cap[9]->LinkCollection = 0x0000
+pp_data->cap[9]->LinkUsagePage = 0x000C
+pp_data->cap[9]->LinkUsage = 0x0001
+pp_data->cap[9]->IsMultipleItemsForArray = 0
+pp_data->cap[9]->IsButtonCap = 1
+pp_data->cap[9]->IsPadding = 0
+pp_data->cap[9]->IsAbsolute = 1
+pp_data->cap[9]->IsRange = 0
+pp_data->cap[9]->IsAlias = 0
+pp_data->cap[9]->IsStringRange = 0
+pp_data->cap[9]->IsDesignatorRange = 0
+pp_data->cap[9]->Reserved1 = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[9]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[9]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[9]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[9]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[9]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[9]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[9]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[9]->NotRange.Usage = 0x0000
+pp_data->cap[9]->NotRange.Reserved1 = 0x0000
+pp_data->cap[9]->NotRange.StringIndex = 0
+pp_data->cap[9]->NotRange.Reserved2 = 0
+pp_data->cap[9]->NotRange.DesignatorIndex = 0
+pp_data->cap[9]->NotRange.Reserved3 = 0
+pp_data->cap[9]->NotRange.DataIndex = 0
+pp_data->cap[9]->NotRange.Reserved4 = 0
+pp_data->cap[9]->Button.LogicalMin = 0
+pp_data->cap[9]->Button.LogicalMax = 0
+pp_data->cap[9]->Units = 0
+pp_data->cap[9]->UnitsExp = 0
+
+pp_data->cap[10]->UsagePage = 0x000C
+pp_data->cap[10]->ReportID = 0x04
+pp_data->cap[10]->BitPosition = 0
+pp_data->cap[10]->BitSize = 8
+pp_data->cap[10]->ReportCount = 36
+pp_data->cap[10]->BytePosition = 0x0001
+pp_data->cap[10]->BitCount = 288
+pp_data->cap[10]->BitField = 0x02
+pp_data->cap[10]->NextBytePosition = 0x0025
+pp_data->cap[10]->LinkCollection = 0x0000
+pp_data->cap[10]->LinkUsagePage = 0x000C
+pp_data->cap[10]->LinkUsage = 0x0001
+pp_data->cap[10]->IsMultipleItemsForArray = 0
+pp_data->cap[10]->IsButtonCap = 0
+pp_data->cap[10]->IsPadding = 0
+pp_data->cap[10]->IsAbsolute = 1
+pp_data->cap[10]->IsRange = 0
+pp_data->cap[10]->IsAlias = 0
+pp_data->cap[10]->IsStringRange = 0
+pp_data->cap[10]->IsDesignatorRange = 0
+pp_data->cap[10]->Reserved1 = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[10]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[10]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[10]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[10]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[10]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[10]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[10]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[10]->NotRange.Usage = 0x0000
+pp_data->cap[10]->NotRange.Reserved1 = 0x0000
+pp_data->cap[10]->NotRange.StringIndex = 0
+pp_data->cap[10]->NotRange.Reserved2 = 0
+pp_data->cap[10]->NotRange.DesignatorIndex = 0
+pp_data->cap[10]->NotRange.Reserved3 = 0
+pp_data->cap[10]->NotRange.DataIndex = 1
+pp_data->cap[10]->NotRange.Reserved4 = 1
+pp_data->cap[10]->NotButton.HasNull = 0
+pp_data->cap[10]->NotButton.Reserved4 = 0x000000
+pp_data->cap[10]->NotButton.LogicalMin = 0
+pp_data->cap[10]->NotButton.LogicalMax = 1
+pp_data->cap[10]->NotButton.PhysicalMin = 0
+pp_data->cap[10]->NotButton.PhysicalMax = 0
+pp_data->cap[10]->Units = 0
+pp_data->cap[10]->UnitsExp = 0
+
+pp_data->cap[11]->UsagePage = 0x000C
+pp_data->cap[11]->ReportID = 0x06
+pp_data->cap[11]->BitPosition = 0
+pp_data->cap[11]->BitSize = 8
+pp_data->cap[11]->ReportCount = 36
+pp_data->cap[11]->BytePosition = 0x0001
+pp_data->cap[11]->BitCount = 288
+pp_data->cap[11]->BitField = 0x02
+pp_data->cap[11]->NextBytePosition = 0x0025
+pp_data->cap[11]->LinkCollection = 0x0000
+pp_data->cap[11]->LinkUsagePage = 0x000C
+pp_data->cap[11]->LinkUsage = 0x0001
+pp_data->cap[11]->IsMultipleItemsForArray = 0
+pp_data->cap[11]->IsButtonCap = 0
+pp_data->cap[11]->IsPadding = 0
+pp_data->cap[11]->IsAbsolute = 1
+pp_data->cap[11]->IsRange = 0
+pp_data->cap[11]->IsAlias = 0
+pp_data->cap[11]->IsStringRange = 0
+pp_data->cap[11]->IsDesignatorRange = 0
+pp_data->cap[11]->Reserved1 = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[11]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[11]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[11]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[11]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[11]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[11]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[11]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[11]->NotRange.Usage = 0x0000
+pp_data->cap[11]->NotRange.Reserved1 = 0x0000
+pp_data->cap[11]->NotRange.StringIndex = 0
+pp_data->cap[11]->NotRange.Reserved2 = 0
+pp_data->cap[11]->NotRange.DesignatorIndex = 0
+pp_data->cap[11]->NotRange.Reserved3 = 0
+pp_data->cap[11]->NotRange.DataIndex = 2
+pp_data->cap[11]->NotRange.Reserved4 = 2
+pp_data->cap[11]->NotButton.HasNull = 0
+pp_data->cap[11]->NotButton.Reserved4 = 0x000000
+pp_data->cap[11]->NotButton.LogicalMin = 0
+pp_data->cap[11]->NotButton.LogicalMax = 1
+pp_data->cap[11]->NotButton.PhysicalMin = 0
+pp_data->cap[11]->NotButton.PhysicalMax = 0
+pp_data->cap[11]->Units = 0
+pp_data->cap[11]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x000C
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 1
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 1
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[1]->LinkUsage = 0x0036
+pp_data->LinkCollectionArray[1]->LinkUsagePage = 0x000C
+pp_data->LinkCollectionArray[1]->Parent = 0
+pp_data->LinkCollectionArray[1]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[1]->NextSibling = 0
+pp_data->LinkCollectionArray[1]->FirstChild = 0
+pp_data->LinkCollectionArray[1]->CollectionType = 2
+pp_data->LinkCollectionArray[1]->IsAlias = 0
+pp_data->LinkCollectionArray[1]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_0A37_0001_000C_expected.rpt_desc b/src/hidapi/windows/test/data/046D_0A37_0001_000C_expected.rpt_desc
new file mode 100644
index 0000000..363a8f5
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_0A37_0001_000C_expected.rpt_desc
@@ -0,0 +1,16 @@
+0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x01, 0x09, 0xE9,
+0x09, 0xEA, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x02,
+0x81, 0x02, 0x09, 0xE2, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01,
+0x95, 0x01, 0x81, 0x06, 0x75, 0x02, 0x95, 0x01, 0x81, 0x03,
+0x09, 0x36, 0xA1, 0x02, 0x05, 0x09, 0x19, 0x01, 0x29, 0x02,
+0x15, 0x01, 0x25, 0x02, 0x75, 0x02, 0x95, 0x01, 0x81, 0x40,
+0x75, 0x01, 0x95, 0x01, 0x81, 0x03, 0xC0, 0x85, 0x02, 0x05,
+0x0C, 0x09, 0x00, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95,
+0x10, 0x81, 0x02, 0x85, 0x05, 0x09, 0x00, 0x15, 0x00, 0x25,
+0x01, 0x75, 0x08, 0x95, 0x20, 0x81, 0x02, 0x85, 0x07, 0x09,
+0x00, 0x15, 0x00, 0x25, 0x01, 0x75, 0x08, 0x95, 0x20, 0x81,
+0x02, 0x85, 0x03, 0x09, 0x00, 0x15, 0x00, 0x25, 0x01, 0x75,
+0x01, 0x95, 0x10, 0x91, 0x02, 0x85, 0x04, 0x09, 0x00, 0x15,
+0x00, 0x25, 0x01, 0x75, 0x08, 0x95, 0x24, 0x91, 0x02, 0x85,
+0x06, 0x09, 0x00, 0x15, 0x00, 0x25, 0x01, 0x75, 0x08, 0x95,
+0x24, 0x91, 0x02, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_0A37_0001_000C_real.rpt_desc b/src/hidapi/windows/test/data/046D_0A37_0001_000C_real.rpt_desc
new file mode 100644
index 0000000..c784743
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_0A37_0001_000C_real.rpt_desc
@@ -0,0 +1,61 @@
+Usage Page (Consumer Devices) 05 0C
+Usage (Consumer Control) 09 01
+Collection (Application) A1 01
+ Report ID (1) 85 01
+ Logical Minimum (0) 15 00
+ Logical Maximum (1) 25 01
+ Usage (Volume Increment) 09 E9
+ Usage (Volume Decrement) 09 EA
+ Report Size (1) 75 01
+ Report Count (2) 95 02
+ Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
+ Usage (Mute) 09 E2
+ Report Count (1) 95 01
+ Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit) 81 06
+ Usage (Undefined) 09 00
+ Report Count (2) 95 02
+ Input (Cnst,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 03
+ Usage (Function Buttons) 09 36
+ Collection (Logical) A1 02
+ Usage Page (Button) 05 09
+ Usage Minimum (Button 1) 19 01
+ Usage Maximum (Button 2) 29 02
+ Report Size (2) 75 02
+ Report Count (1) 95 01
+ Logical Minimum (1) 15 01
+ Logical Maximum (2) 25 02
+ Input (Data,Ary,Abs) 81 40
+ End Collection C0
+ Usage Page (Consumer Devices) 05 0C
+ Usage (Undefined) 09 00
+ Logical Minimum (0) 15 00
+ Logical Maximum (1) 25 01
+ Report Size (1) 75 01
+ Report Count (1) 95 01
+ Input (Cnst,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 03
+ Report ID (2) 85 02
+ Usage Page (Consumer Devices) 05 0C
+ Usage (Undefined) 09 00
+ Report Count (16) 95 10
+ Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
+ Report ID (3) 85 03
+ Usage (Undefined) 09 00
+ Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 02
+ Report ID (4) 85 04
+ Usage (Undefined) 09 00
+ Report Size (8) 75 08
+ Report Count (36) 95 24
+ Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 02
+ Report ID (5) 85 05
+ Usage (Undefined) 09 00
+ Report Count (32) 95 20
+ Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
+ Report ID (6) 85 06
+ Usage (Undefined) 09 00
+ Report Count (36) 95 24
+ Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 02
+ Report ID (7) 85 07
+ Usage (Undefined) 09 00
+ Report Count (32) 95 20
+ Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
+End Collection C0
diff --git a/src/hidapi/windows/test/data/046D_B010_0001_000C.pp_data b/src/hidapi/windows/test/data/046D_B010_0001_000C.pp_data
new file mode 100644
index 0000000..047445b
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0001_000C.pp_data
@@ -0,0 +1,97 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xB010
+dev->manufacturer_string = "Logitech"
+dev->product_string = "Logitech Bluetooth Wireless Mouse"
+dev->release_number = 0x0000
+dev->interface_number = -1
+dev->usage = 0x0001
+dev->usage_page = 0x000C
+dev->path = "\\?\hid#{00001124-0000-1000-8000-00805f9b34fb}_vid&0002046d_pid&b010&col02#8&1cf1c1b9&3&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0001
+pp_data->UsagePage = 0x000C
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 1
+pp_data->caps_info[0]->NumberOfCaps = 1
+pp_data->caps_info[0]->ReportByteLength = 2
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 1
+pp_data->caps_info[1]->LastCap = 1
+pp_data->caps_info[1]->NumberOfCaps = 0
+pp_data->caps_info[1]->ReportByteLength = 0
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 1
+pp_data->caps_info[2]->LastCap = 1
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x0068
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x0006
+pp_data->cap[0]->ReportID = 0x03
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 8
+pp_data->cap[0]->ReportCount = 1
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 8
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0002
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0x000C
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 0
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0020
+pp_data->cap[0]->NotRange.Reserved1 = 0x0020
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->NotButton.HasNull = 0
+pp_data->cap[0]->NotButton.Reserved4 = 0x000000
+pp_data->cap[0]->NotButton.LogicalMin = 0
+pp_data->cap[0]->NotButton.LogicalMax = 100
+pp_data->cap[0]->NotButton.PhysicalMin = 0
+pp_data->cap[0]->NotButton.PhysicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x000C
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_B010_0001_000C_expected.rpt_desc b/src/hidapi/windows/test/data/046D_B010_0001_000C_expected.rpt_desc
new file mode 100644
index 0000000..c80dd13
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0001_000C_expected.rpt_desc
@@ -0,0 +1,3 @@
+0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x03, 0x05, 0x06,
+0x09, 0x20, 0x15, 0x00, 0x25, 0x64, 0x75, 0x08, 0x95, 0x01,
+0x81, 0x02, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_B010_0001_000C_real.rpt_desc b/src/hidapi/windows/test/data/046D_B010_0001_000C_real.rpt_desc
new file mode 100644
index 0000000..ff019a9
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0001_000C_real.rpt_desc
@@ -0,0 +1,38 @@
+
+mac-hid-dump on main ❯ ./mac-hid-dump
+mac-hid-dump:
+...
+046D B010: Unknown - Bluetooth Mouse M557
+DESCRIPTOR:
+ 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01
+ 29 08 15 00 25 01 75 01 95 08 81 02 05 01 09 30
+ 09 31 16 01 f8 26 ff 07 75 0c 95 02 81 06 09 38
+ 15 81 25 7f 75 08 95 01 81 06 05 0c 0a 38 02 75
+ 08 95 01 81 06 c0 c0 05 0c 09 01 a1 01 85 03 05
+ 06 09 20 15 00 26 64 00 75 08 95 01 81 02 c0 06
+ 00 ff 09 01 a1 01 85 10 75 08 95 06 15 00 26 ff
+ 00 09 01 81 00 09 01 91 00 c0 06 00 ff 09 02 a1
+ 01 85 11 75 08 95 13 15 00 26 ff 00 09 02 81 00
+ 09 02 91 00 c0 05 01 09 06 a1 01 85 04 75 01 95
+ 08 05 07 19 e0 29 e7 15 00 25 01 81 02 95 01 75
+ 08 81 03 95 05 75 01 05 08 19 01 29 05 91 02 95
+ 01 75 03 91 03 95 06 75 08 15 00 26 ff 00 05 07
+ 19 00 29 ff 81 00 c0 05 0c 09 01 a1 01 85 05 15
+ 00 25 01 75 01 95 02 0a 25 02 0a 24 02 81 02 95
+ 01 75 06 81 03 c0
+ (246 bytes)
+
+Parser output:
+
+0x05, 0x0C, // Usage Page (Consumer)
+0x09, 0x01, // Usage (Consumer Control)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x03, // Report ID (3)
+0x05, 0x06, // Usage Page (Generic Dev Ctrls)
+0x09, 0x20, // Usage (Battery Strength)
+0x15, 0x00, // Logical Minimum (0)
+0x26, 0x64, 0x00, // Logical Maximum (100)
+0x75, 0x08, // Report Size (8)
+0x95, 0x01, // Report Count (1)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
diff --git a/src/hidapi/windows/test/data/046D_B010_0001_FF00.pp_data b/src/hidapi/windows/test/data/046D_B010_0001_FF00.pp_data
new file mode 100644
index 0000000..13b27da
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0001_FF00.pp_data
@@ -0,0 +1,139 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xB010
+dev->manufacturer_string = "Logitech"
+dev->product_string = "Logitech Bluetooth Wireless Mouse"
+dev->release_number = 0x0000
+dev->interface_number = -1
+dev->usage = 0x0001
+dev->usage_page = 0xFF00
+dev->path = "\\?\hid#{00001124-0000-1000-8000-00805f9b34fb}_vid&0002046d_pid&b010&col03#8&1cf1c1b9&3&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0001
+pp_data->UsagePage = 0xFF00
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 1
+pp_data->caps_info[0]->NumberOfCaps = 1
+pp_data->caps_info[0]->ReportByteLength = 7
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 1
+pp_data->caps_info[1]->LastCap = 2
+pp_data->caps_info[1]->NumberOfCaps = 1
+pp_data->caps_info[1]->ReportByteLength = 7
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 2
+pp_data->caps_info[2]->LastCap = 2
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x00D0
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0xFF00
+pp_data->cap[0]->ReportID = 0x10
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 8
+pp_data->cap[0]->ReportCount = 6
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 48
+pp_data->cap[0]->BitField = 0x00
+pp_data->cap[0]->NextBytePosition = 0x0007
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0xFF00
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0001
+pp_data->cap[0]->NotRange.Reserved1 = 0x0001
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 255
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[1]->UsagePage = 0xFF00
+pp_data->cap[1]->ReportID = 0x10
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 8
+pp_data->cap[1]->ReportCount = 6
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 48
+pp_data->cap[1]->BitField = 0x00
+pp_data->cap[1]->NextBytePosition = 0x0007
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0xFF00
+pp_data->cap[1]->LinkUsage = 0x0001
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0001
+pp_data->cap[1]->NotRange.Reserved1 = 0x0001
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 0
+pp_data->cap[1]->NotRange.Reserved4 = 0
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 255
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0xFF00
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_B010_0001_FF00_expected.rpt_desc b/src/hidapi/windows/test/data/046D_B010_0001_FF00_expected.rpt_desc
new file mode 100644
index 0000000..812bd2a
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0001_FF00_expected.rpt_desc
@@ -0,0 +1,4 @@
+0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x10, 0x09,
+0x01, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x06,
+0x81, 0x00, 0x09, 0x01, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x06, 0x91, 0x00, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_B010_0001_FF00_real.rpt_desc b/src/hidapi/windows/test/data/046D_B010_0001_FF00_real.rpt_desc
new file mode 100644
index 0000000..340d08d
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0001_FF00_real.rpt_desc
@@ -0,0 +1,39 @@
+
+mac-hid-dump on main ❯ ./mac-hid-dump
+mac-hid-dump:
+...
+046D B010: Unknown - Bluetooth Mouse M557
+DESCRIPTOR:
+ 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01
+ 29 08 15 00 25 01 75 01 95 08 81 02 05 01 09 30
+ 09 31 16 01 f8 26 ff 07 75 0c 95 02 81 06 09 38
+ 15 81 25 7f 75 08 95 01 81 06 05 0c 0a 38 02 75
+ 08 95 01 81 06 c0 c0 05 0c 09 01 a1 01 85 03 05
+ 06 09 20 15 00 26 64 00 75 08 95 01 81 02 c0 06
+ 00 ff 09 01 a1 01 85 10 75 08 95 06 15 00 26 ff
+ 00 09 01 81 00 09 01 91 00 c0 06 00 ff 09 02 a1
+ 01 85 11 75 08 95 13 15 00 26 ff 00 09 02 81 00
+ 09 02 91 00 c0 05 01 09 06 a1 01 85 04 75 01 95
+ 08 05 07 19 e0 29 e7 15 00 25 01 81 02 95 01 75
+ 08 81 03 95 05 75 01 05 08 19 01 29 05 91 02 95
+ 01 75 03 91 03 95 06 75 08 15 00 26 ff 00 05 07
+ 19 00 29 ff 81 00 c0 05 0c 09 01 a1 01 85 05 15
+ 00 25 01 75 01 95 02 0a 25 02 0a 24 02 81 02 95
+ 01 75 06 81 03 c0
+ (246 bytes)
+
+Parser output:
+
+0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
+0x09, 0x01, // Usage (0x01)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x10, // Report ID (16)
+0x75, 0x08, // Report Size (8)
+0x95, 0x06, // Report Count (6)
+0x15, 0x00, // Logical Minimum (0)
+0x26, 0xFF, 0x00, // Logical Maximum (255)
+0x09, 0x01, // Usage (0x01)
+0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x09, 0x01, // Usage (0x01)
+0x91, 0x00, // Output (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0xC0, // End Collection
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_B010_0002_0001.pp_data b/src/hidapi/windows/test/data/046D_B010_0002_0001.pp_data
new file mode 100644
index 0000000..1976766
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0002_0001.pp_data
@@ -0,0 +1,302 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xB010
+dev->manufacturer_string = "Logitech"
+dev->product_string = "Logitech Bluetooth Wireless Mouse"
+dev->release_number = 0x0000
+dev->interface_number = -1
+dev->usage = 0x0002
+dev->usage_page = 0x0001
+dev->path = "\\?\hid#{00001124-0000-1000-8000-00805f9b34fb}_vid&0002046d_pid&b010&col01#8&1cf1c1b9&3&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0002
+pp_data->UsagePage = 0x0001
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 5
+pp_data->caps_info[0]->NumberOfCaps = 5
+pp_data->caps_info[0]->ReportByteLength = 7
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 5
+pp_data->caps_info[1]->LastCap = 5
+pp_data->caps_info[1]->NumberOfCaps = 0
+pp_data->caps_info[1]->ReportByteLength = 0
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 5
+pp_data->caps_info[2]->LastCap = 5
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x0208
+pp_data->NumberLinkCollectionNodes = 2
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x0009
+pp_data->cap[0]->ReportID = 0x02
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 1
+pp_data->cap[0]->ReportCount = 8
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 8
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0002
+pp_data->cap[0]->LinkCollection = 0x0001
+pp_data->cap[0]->LinkUsagePage = 0x0001
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 1
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->Range.UsageMin = 0x0001
+pp_data->cap[0]->Range.UsageMax = 0x0008
+pp_data->cap[0]->Range.StringMin = 0
+pp_data->cap[0]->Range.StringMax = 0
+pp_data->cap[0]->Range.DesignatorMin = 0
+pp_data->cap[0]->Range.DesignatorMax = 0
+pp_data->cap[0]->Range.DataIndexMin = 0
+pp_data->cap[0]->Range.DataIndexMax = 7
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x0001
+pp_data->cap[1]->ReportID = 0x02
+pp_data->cap[1]->BitPosition = 4
+pp_data->cap[1]->BitSize = 12
+pp_data->cap[1]->ReportCount = 1
+pp_data->cap[1]->BytePosition = 0x0003
+pp_data->cap[1]->BitCount = 12
+pp_data->cap[1]->BitField = 0x06
+pp_data->cap[1]->NextBytePosition = 0x0005
+pp_data->cap[1]->LinkCollection = 0x0001
+pp_data->cap[1]->LinkUsagePage = 0x0001
+pp_data->cap[1]->LinkUsage = 0x0001
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 0
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 0
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0031
+pp_data->cap[1]->NotRange.Reserved1 = 0x0031
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 8
+pp_data->cap[1]->NotRange.Reserved4 = 8
+pp_data->cap[1]->NotButton.HasNull = 0
+pp_data->cap[1]->NotButton.Reserved4 = 0x000000
+pp_data->cap[1]->NotButton.LogicalMin = -2047
+pp_data->cap[1]->NotButton.LogicalMax = 2047
+pp_data->cap[1]->NotButton.PhysicalMin = 0
+pp_data->cap[1]->NotButton.PhysicalMax = 0
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+pp_data->cap[2]->UsagePage = 0x0001
+pp_data->cap[2]->ReportID = 0x02
+pp_data->cap[2]->BitPosition = 0
+pp_data->cap[2]->BitSize = 12
+pp_data->cap[2]->ReportCount = 1
+pp_data->cap[2]->BytePosition = 0x0002
+pp_data->cap[2]->BitCount = 12
+pp_data->cap[2]->BitField = 0x06
+pp_data->cap[2]->NextBytePosition = 0x0004
+pp_data->cap[2]->LinkCollection = 0x0001
+pp_data->cap[2]->LinkUsagePage = 0x0001
+pp_data->cap[2]->LinkUsage = 0x0001
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 0
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 0
+pp_data->cap[2]->IsRange = 0
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->NotRange.Usage = 0x0030
+pp_data->cap[2]->NotRange.Reserved1 = 0x0030
+pp_data->cap[2]->NotRange.StringIndex = 0
+pp_data->cap[2]->NotRange.Reserved2 = 0
+pp_data->cap[2]->NotRange.DesignatorIndex = 0
+pp_data->cap[2]->NotRange.Reserved3 = 0
+pp_data->cap[2]->NotRange.DataIndex = 9
+pp_data->cap[2]->NotRange.Reserved4 = 9
+pp_data->cap[2]->NotButton.HasNull = 0
+pp_data->cap[2]->NotButton.Reserved4 = 0x000000
+pp_data->cap[2]->NotButton.LogicalMin = -2047
+pp_data->cap[2]->NotButton.LogicalMax = 2047
+pp_data->cap[2]->NotButton.PhysicalMin = 0
+pp_data->cap[2]->NotButton.PhysicalMax = 0
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+pp_data->cap[3]->UsagePage = 0x0001
+pp_data->cap[3]->ReportID = 0x02
+pp_data->cap[3]->BitPosition = 0
+pp_data->cap[3]->BitSize = 8
+pp_data->cap[3]->ReportCount = 1
+pp_data->cap[3]->BytePosition = 0x0005
+pp_data->cap[3]->BitCount = 8
+pp_data->cap[3]->BitField = 0x06
+pp_data->cap[3]->NextBytePosition = 0x0006
+pp_data->cap[3]->LinkCollection = 0x0001
+pp_data->cap[3]->LinkUsagePage = 0x0001
+pp_data->cap[3]->LinkUsage = 0x0001
+pp_data->cap[3]->IsMultipleItemsForArray = 0
+pp_data->cap[3]->IsButtonCap = 0
+pp_data->cap[3]->IsPadding = 0
+pp_data->cap[3]->IsAbsolute = 0
+pp_data->cap[3]->IsRange = 0
+pp_data->cap[3]->IsAlias = 0
+pp_data->cap[3]->IsStringRange = 0
+pp_data->cap[3]->IsDesignatorRange = 0
+pp_data->cap[3]->Reserved1 = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[3]->NotRange.Usage = 0x0038
+pp_data->cap[3]->NotRange.Reserved1 = 0x0038
+pp_data->cap[3]->NotRange.StringIndex = 0
+pp_data->cap[3]->NotRange.Reserved2 = 0
+pp_data->cap[3]->NotRange.DesignatorIndex = 0
+pp_data->cap[3]->NotRange.Reserved3 = 0
+pp_data->cap[3]->NotRange.DataIndex = 10
+pp_data->cap[3]->NotRange.Reserved4 = 10
+pp_data->cap[3]->NotButton.HasNull = 0
+pp_data->cap[3]->NotButton.Reserved4 = 0x000000
+pp_data->cap[3]->NotButton.LogicalMin = -127
+pp_data->cap[3]->NotButton.LogicalMax = 127
+pp_data->cap[3]->NotButton.PhysicalMin = 0
+pp_data->cap[3]->NotButton.PhysicalMax = 0
+pp_data->cap[3]->Units = 0
+pp_data->cap[3]->UnitsExp = 0
+
+pp_data->cap[4]->UsagePage = 0x000C
+pp_data->cap[4]->ReportID = 0x02
+pp_data->cap[4]->BitPosition = 0
+pp_data->cap[4]->BitSize = 8
+pp_data->cap[4]->ReportCount = 1
+pp_data->cap[4]->BytePosition = 0x0006
+pp_data->cap[4]->BitCount = 8
+pp_data->cap[4]->BitField = 0x06
+pp_data->cap[4]->NextBytePosition = 0x0007
+pp_data->cap[4]->LinkCollection = 0x0001
+pp_data->cap[4]->LinkUsagePage = 0x0001
+pp_data->cap[4]->LinkUsage = 0x0001
+pp_data->cap[4]->IsMultipleItemsForArray = 0
+pp_data->cap[4]->IsButtonCap = 0
+pp_data->cap[4]->IsPadding = 0
+pp_data->cap[4]->IsAbsolute = 0
+pp_data->cap[4]->IsRange = 0
+pp_data->cap[4]->IsAlias = 0
+pp_data->cap[4]->IsStringRange = 0
+pp_data->cap[4]->IsDesignatorRange = 0
+pp_data->cap[4]->Reserved1 = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[4]->NotRange.Usage = 0x0238
+pp_data->cap[4]->NotRange.Reserved1 = 0x0238
+pp_data->cap[4]->NotRange.StringIndex = 0
+pp_data->cap[4]->NotRange.Reserved2 = 0
+pp_data->cap[4]->NotRange.DesignatorIndex = 0
+pp_data->cap[4]->NotRange.Reserved3 = 0
+pp_data->cap[4]->NotRange.DataIndex = 11
+pp_data->cap[4]->NotRange.Reserved4 = 11
+pp_data->cap[4]->NotButton.HasNull = 0
+pp_data->cap[4]->NotButton.Reserved4 = 0x000000
+pp_data->cap[4]->NotButton.LogicalMin = -127
+pp_data->cap[4]->NotButton.LogicalMax = 127
+pp_data->cap[4]->NotButton.PhysicalMin = 0
+pp_data->cap[4]->NotButton.PhysicalMax = 0
+pp_data->cap[4]->Units = 0
+pp_data->cap[4]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0002
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 1
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 1
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[1]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[1]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[1]->Parent = 0
+pp_data->LinkCollectionArray[1]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[1]->NextSibling = 0
+pp_data->LinkCollectionArray[1]->FirstChild = 0
+pp_data->LinkCollectionArray[1]->CollectionType = 0
+pp_data->LinkCollectionArray[1]->IsAlias = 0
+pp_data->LinkCollectionArray[1]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_B010_0002_0001_expected.rpt_desc b/src/hidapi/windows/test/data/046D_B010_0002_0001_expected.rpt_desc
new file mode 100644
index 0000000..d782fa1
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0002_0001_expected.rpt_desc
@@ -0,0 +1,8 @@
+0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, 0x00,
+0x85, 0x02, 0x05, 0x09, 0x19, 0x01, 0x29, 0x08, 0x15, 0x00,
+0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x05, 0x01,
+0x09, 0x30, 0x09, 0x31, 0x16, 0x01, 0xF8, 0x26, 0xFF, 0x07,
+0x75, 0x0C, 0x95, 0x02, 0x81, 0x06, 0x09, 0x38, 0x15, 0x81,
+0x25, 0x7F, 0x75, 0x08, 0x95, 0x01, 0x81, 0x06, 0x05, 0x0C,
+0x0A, 0x38, 0x02, 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95,
+0x01, 0x81, 0x06, 0xC0, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_B010_0002_0001_real.rpt_desc b/src/hidapi/windows/test/data/046D_B010_0002_0001_real.rpt_desc
new file mode 100644
index 0000000..483f659
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0002_0001_real.rpt_desc
@@ -0,0 +1,61 @@
+
+mac-hid-dump on main ❯ ./mac-hid-dump
+mac-hid-dump:
+...
+046D B010: Unknown - Bluetooth Mouse M557
+DESCRIPTOR:
+ 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01
+ 29 08 15 00 25 01 75 01 95 08 81 02 05 01 09 30
+ 09 31 16 01 f8 26 ff 07 75 0c 95 02 81 06 09 38
+ 15 81 25 7f 75 08 95 01 81 06 05 0c 0a 38 02 75
+ 08 95 01 81 06 c0 c0 05 0c 09 01 a1 01 85 03 05
+ 06 09 20 15 00 26 64 00 75 08 95 01 81 02 c0 06
+ 00 ff 09 01 a1 01 85 10 75 08 95 06 15 00 26 ff
+ 00 09 01 81 00 09 01 91 00 c0 06 00 ff 09 02 a1
+ 01 85 11 75 08 95 13 15 00 26 ff 00 09 02 81 00
+ 09 02 91 00 c0 05 01 09 06 a1 01 85 04 75 01 95
+ 08 05 07 19 e0 29 e7 15 00 25 01 81 02 95 01 75
+ 08 81 03 95 05 75 01 05 08 19 01 29 05 91 02 95
+ 01 75 03 91 03 95 06 75 08 15 00 26 ff 00 05 07
+ 19 00 29 ff 81 00 c0 05 0c 09 01 a1 01 85 05 15
+ 00 25 01 75 01 95 02 0a 25 02 0a 24 02 81 02 95
+ 01 75 06 81 03 c0
+ (246 bytes)
+
+Parser output:
+
+0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+0x09, 0x02, // Usage (Mouse)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x02, // Report ID (2)
+0x09, 0x01, // Usage (Pointer)
+0xA1, 0x00, // Collection (Physical)
+0x05, 0x09, // Usage Page (Button)
+0x19, 0x01, // Usage Minimum (0x01)
+0x29, 0x08, // Usage Maximum (0x08)
+0x15, 0x00, // Logical Minimum (0)
+0x25, 0x01, // Logical Maximum (1)
+0x75, 0x01, // Report Size (1)
+0x95, 0x08, // Report Count (8)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+0x09, 0x30, // Usage (X)
+0x09, 0x31, // Usage (Y)
+0x16, 0x01, 0xF8, // Logical Minimum (-2047)
+0x26, 0xFF, 0x07, // Logical Maximum (2047)
+0x75, 0x0C, // Report Size (12)
+0x95, 0x02, // Report Count (2)
+0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+0x09, 0x38, // Usage (Wheel)
+0x15, 0x81, // Logical Minimum (-127)
+0x25, 0x7F, // Logical Maximum (127)
+0x75, 0x08, // Report Size (8)
+0x95, 0x01, // Report Count (1)
+0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+0x05, 0x0C, // Usage Page (Consumer)
+0x0A, 0x38, 0x02, // Usage (AC Pan)
+0x75, 0x08, // Report Size (8)
+0x95, 0x01, // Report Count (1)
+0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
+0xC0, // End Collection
diff --git a/src/hidapi/windows/test/data/046D_B010_0002_FF00.pp_data b/src/hidapi/windows/test/data/046D_B010_0002_FF00.pp_data
new file mode 100644
index 0000000..0dc64b2
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0002_FF00.pp_data
@@ -0,0 +1,139 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xB010
+dev->manufacturer_string = "Logitech"
+dev->product_string = "Logitech Bluetooth Wireless Mouse"
+dev->release_number = 0x0000
+dev->interface_number = -1
+dev->usage = 0x0002
+dev->usage_page = 0xFF00
+dev->path = "\\?\hid#{00001124-0000-1000-8000-00805f9b34fb}_vid&0002046d_pid&b010&col04#8&1cf1c1b9&3&0003#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0002
+pp_data->UsagePage = 0xFF00
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 1
+pp_data->caps_info[0]->NumberOfCaps = 1
+pp_data->caps_info[0]->ReportByteLength = 20
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 1
+pp_data->caps_info[1]->LastCap = 2
+pp_data->caps_info[1]->NumberOfCaps = 1
+pp_data->caps_info[1]->ReportByteLength = 20
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 2
+pp_data->caps_info[2]->LastCap = 2
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x00D0
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0xFF00
+pp_data->cap[0]->ReportID = 0x11
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 8
+pp_data->cap[0]->ReportCount = 19
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 152
+pp_data->cap[0]->BitField = 0x00
+pp_data->cap[0]->NextBytePosition = 0x0014
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0xFF00
+pp_data->cap[0]->LinkUsage = 0x0002
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0002
+pp_data->cap[0]->NotRange.Reserved1 = 0x0002
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 255
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[1]->UsagePage = 0xFF00
+pp_data->cap[1]->ReportID = 0x11
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 8
+pp_data->cap[1]->ReportCount = 19
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 152
+pp_data->cap[1]->BitField = 0x00
+pp_data->cap[1]->NextBytePosition = 0x0014
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0xFF00
+pp_data->cap[1]->LinkUsage = 0x0002
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0002
+pp_data->cap[1]->NotRange.Reserved1 = 0x0002
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 0
+pp_data->cap[1]->NotRange.Reserved4 = 0
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 255
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0002
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0xFF00
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_B010_0002_FF00_expected.rpt_desc b/src/hidapi/windows/test/data/046D_B010_0002_FF00_expected.rpt_desc
new file mode 100644
index 0000000..b1654e7
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0002_FF00_expected.rpt_desc
@@ -0,0 +1,4 @@
+0x06, 0x00, 0xFF, 0x09, 0x02, 0xA1, 0x01, 0x85, 0x11, 0x09,
+0x02, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x13,
+0x81, 0x00, 0x09, 0x02, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x13, 0x91, 0x00, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_B010_0002_FF00_real.rpt_desc b/src/hidapi/windows/test/data/046D_B010_0002_FF00_real.rpt_desc
new file mode 100644
index 0000000..8b8dbfc
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0002_FF00_real.rpt_desc
@@ -0,0 +1,39 @@
+
+mac-hid-dump on main ❯ ./mac-hid-dump
+mac-hid-dump:
+...
+046D B010: Unknown - Bluetooth Mouse M557
+DESCRIPTOR:
+ 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01
+ 29 08 15 00 25 01 75 01 95 08 81 02 05 01 09 30
+ 09 31 16 01 f8 26 ff 07 75 0c 95 02 81 06 09 38
+ 15 81 25 7f 75 08 95 01 81 06 05 0c 0a 38 02 75
+ 08 95 01 81 06 c0 c0 05 0c 09 01 a1 01 85 03 05
+ 06 09 20 15 00 26 64 00 75 08 95 01 81 02 c0 06
+ 00 ff 09 01 a1 01 85 10 75 08 95 06 15 00 26 ff
+ 00 09 01 81 00 09 01 91 00 c0 06 00 ff 09 02 a1
+ 01 85 11 75 08 95 13 15 00 26 ff 00 09 02 81 00
+ 09 02 91 00 c0 05 01 09 06 a1 01 85 04 75 01 95
+ 08 05 07 19 e0 29 e7 15 00 25 01 81 02 95 01 75
+ 08 81 03 95 05 75 01 05 08 19 01 29 05 91 02 95
+ 01 75 03 91 03 95 06 75 08 15 00 26 ff 00 05 07
+ 19 00 29 ff 81 00 c0 05 0c 09 01 a1 01 85 05 15
+ 00 25 01 75 01 95 02 0a 25 02 0a 24 02 81 02 95
+ 01 75 06 81 03 c0
+ (246 bytes)
+
+Parser output:
+
+0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
+0x09, 0x02, // Usage (0x02)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x11, // Report ID (17)
+0x75, 0x08, // Report Size (8)
+0x95, 0x13, // Report Count (19)
+0x15, 0x00, // Logical Minimum (0)
+0x26, 0xFF, 0x00, // Logical Maximum (255)
+0x09, 0x02, // Usage (0x02)
+0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x09, 0x02, // Usage (0x02)
+0x91, 0x00, // Output (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0xC0, // End Collection
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_B010_0006_0001.pp_data b/src/hidapi/windows/test/data/046D_B010_0006_0001.pp_data
new file mode 100644
index 0000000..7682a0d
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0006_0001.pp_data
@@ -0,0 +1,185 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xB010
+dev->manufacturer_string = "Logitech"
+dev->product_string = "Logitech Bluetooth Wireless Mouse"
+dev->release_number = 0x0000
+dev->interface_number = -1
+dev->usage = 0x0006
+dev->usage_page = 0x0001
+dev->path = "\\?\hid#{00001124-0000-1000-8000-00805f9b34fb}_vid&0002046d_pid&b010&col05#8&1cf1c1b9&3&0004#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0006
+pp_data->UsagePage = 0x0001
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 2
+pp_data->caps_info[0]->NumberOfCaps = 2
+pp_data->caps_info[0]->ReportByteLength = 9
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 2
+pp_data->caps_info[1]->LastCap = 3
+pp_data->caps_info[1]->NumberOfCaps = 1
+pp_data->caps_info[1]->ReportByteLength = 2
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 3
+pp_data->caps_info[2]->LastCap = 3
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x0138
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x0007
+pp_data->cap[0]->ReportID = 0x04
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 1
+pp_data->cap[0]->ReportCount = 8
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 8
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0002
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0x0001
+pp_data->cap[0]->LinkUsage = 0x0006
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 1
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->Range.UsageMin = 0x00E0
+pp_data->cap[0]->Range.UsageMax = 0x00E7
+pp_data->cap[0]->Range.StringMin = 0
+pp_data->cap[0]->Range.StringMax = 0
+pp_data->cap[0]->Range.DesignatorMin = 0
+pp_data->cap[0]->Range.DesignatorMax = 0
+pp_data->cap[0]->Range.DataIndexMin = 0
+pp_data->cap[0]->Range.DataIndexMax = 7
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x0007
+pp_data->cap[1]->ReportID = 0x04
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 8
+pp_data->cap[1]->ReportCount = 6
+pp_data->cap[1]->BytePosition = 0x0003
+pp_data->cap[1]->BitCount = 48
+pp_data->cap[1]->BitField = 0x00
+pp_data->cap[1]->NextBytePosition = 0x0009
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0x0001
+pp_data->cap[1]->LinkUsage = 0x0006
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 1
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->Range.UsageMin = 0x0000
+pp_data->cap[1]->Range.UsageMax = 0x00FF
+pp_data->cap[1]->Range.StringMin = 0
+pp_data->cap[1]->Range.StringMax = 0
+pp_data->cap[1]->Range.DesignatorMin = 0
+pp_data->cap[1]->Range.DesignatorMax = 0
+pp_data->cap[1]->Range.DataIndexMin = 8
+pp_data->cap[1]->Range.DataIndexMax = 263
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 255
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[2]->UsagePage = 0x0008
+pp_data->cap[2]->ReportID = 0x04
+pp_data->cap[2]->BitPosition = 0
+pp_data->cap[2]->BitSize = 1
+pp_data->cap[2]->ReportCount = 5
+pp_data->cap[2]->BytePosition = 0x0001
+pp_data->cap[2]->BitCount = 5
+pp_data->cap[2]->BitField = 0x02
+pp_data->cap[2]->NextBytePosition = 0x0002
+pp_data->cap[2]->LinkCollection = 0x0000
+pp_data->cap[2]->LinkUsagePage = 0x0001
+pp_data->cap[2]->LinkUsage = 0x0006
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 1
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 1
+pp_data->cap[2]->IsRange = 1
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->Range.UsageMin = 0x0001
+pp_data->cap[2]->Range.UsageMax = 0x0005
+pp_data->cap[2]->Range.StringMin = 0
+pp_data->cap[2]->Range.StringMax = 0
+pp_data->cap[2]->Range.DesignatorMin = 0
+pp_data->cap[2]->Range.DesignatorMax = 0
+pp_data->cap[2]->Range.DataIndexMin = 0
+pp_data->cap[2]->Range.DataIndexMax = 4
+pp_data->cap[2]->Button.LogicalMin = 0
+pp_data->cap[2]->Button.LogicalMax = 0
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0006
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_B010_0006_0001_expected.rpt_desc b/src/hidapi/windows/test/data/046D_B010_0006_0001_expected.rpt_desc
new file mode 100644
index 0000000..1ec0b16
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0006_0001_expected.rpt_desc
@@ -0,0 +1,7 @@
+0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x85, 0x04, 0x05, 0x07,
+0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01,
+0x95, 0x08, 0x81, 0x02, 0x75, 0x08, 0x95, 0x01, 0x81, 0x03,
+0x19, 0x00, 0x29, 0xFF, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x06, 0x81, 0x00, 0x05, 0x08, 0x19, 0x01, 0x29,
+0x05, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x05, 0x91,
+0x02, 0x75, 0x03, 0x95, 0x01, 0x91, 0x03, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_B010_0006_0001_real.rpt_desc b/src/hidapi/windows/test/data/046D_B010_0006_0001_real.rpt_desc
new file mode 100644
index 0000000..59ab03d
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_B010_0006_0001_real.rpt_desc
@@ -0,0 +1,58 @@
+
+mac-hid-dump on main ❯ ./mac-hid-dump
+mac-hid-dump:
+...
+046D B010: Unknown - Bluetooth Mouse M557
+DESCRIPTOR:
+ 05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01
+ 29 08 15 00 25 01 75 01 95 08 81 02 05 01 09 30
+ 09 31 16 01 f8 26 ff 07 75 0c 95 02 81 06 09 38
+ 15 81 25 7f 75 08 95 01 81 06 05 0c 0a 38 02 75
+ 08 95 01 81 06 c0 c0 05 0c 09 01 a1 01 85 03 05
+ 06 09 20 15 00 26 64 00 75 08 95 01 81 02 c0 06
+ 00 ff 09 01 a1 01 85 10 75 08 95 06 15 00 26 ff
+ 00 09 01 81 00 09 01 91 00 c0 06 00 ff 09 02 a1
+ 01 85 11 75 08 95 13 15 00 26 ff 00 09 02 81 00
+ 09 02 91 00 c0 05 01 09 06 a1 01 85 04 75 01 95
+ 08 05 07 19 e0 29 e7 15 00 25 01 81 02 95 01 75
+ 08 81 03 95 05 75 01 05 08 19 01 29 05 91 02 95
+ 01 75 03 91 03 95 06 75 08 15 00 26 ff 00 05 07
+ 19 00 29 ff 81 00 c0 05 0c 09 01 a1 01 85 05 15
+ 00 25 01 75 01 95 02 0a 25 02 0a 24 02 81 02 95
+ 01 75 06 81 03 c0
+ (246 bytes)
+
+Parser output:
+0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+0x09, 0x06, // Usage (Keyboard)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x04, // Report ID (4)
+0x75, 0x01, // Report Size (1)
+0x95, 0x08, // Report Count (8)
+0x05, 0x07, // Usage Page (Kbrd/Keypad)
+0x19, 0xE0, // Usage Minimum (0xE0)
+0x29, 0xE7, // Usage Maximum (0xE7)
+0x15, 0x00, // Logical Minimum (0)
+0x25, 0x01, // Logical Maximum (1)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x95, 0x01, // Report Count (1)
+0x75, 0x08, // Report Size (8)
+0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x95, 0x05, // Report Count (5)
+0x75, 0x01, // Report Size (1)
+0x05, 0x08, // Usage Page (LEDs)
+0x19, 0x01, // Usage Minimum (Num Lock)
+0x29, 0x05, // Usage Maximum (Kana)
+0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x95, 0x01, // Report Count (1)
+0x75, 0x03, // Report Size (3)
+0x91, 0x03, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x95, 0x06, // Report Count (6)
+0x75, 0x08, // Report Size (8)
+0x15, 0x00, // Logical Minimum (0)
+0x26, 0xFF, 0x00, // Logical Maximum (255)
+0x05, 0x07, // Usage Page (Kbrd/Keypad)
+0x19, 0x00, // Usage Minimum (0x00)
+0x29, 0xFF, // Usage Maximum (0xFF)
+0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C077_0002_0001.pp_data b/src/hidapi/windows/test/data/046D_C077_0002_0001.pp_data
new file mode 100644
index 0000000..3e9fcea
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C077_0002_0001.pp_data
@@ -0,0 +1,252 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xC077
+dev->manufacturer_string = "Logitech"
+dev->product_string = "USB Optical Mouse"
+dev->release_number = 0x7200
+dev->interface_number = -1
+dev->usage = 0x0002
+dev->usage_page = 0x0001
+dev->path = "\\?\hid#vid_046d&pid_c077#7&1875dbae&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0002
+pp_data->UsagePage = 0x0001
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 4
+pp_data->caps_info[0]->NumberOfCaps = 4
+pp_data->caps_info[0]->ReportByteLength = 5
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 4
+pp_data->caps_info[1]->LastCap = 4
+pp_data->caps_info[1]->NumberOfCaps = 0
+pp_data->caps_info[1]->ReportByteLength = 0
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 4
+pp_data->caps_info[2]->LastCap = 4
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x01A0
+pp_data->NumberLinkCollectionNodes = 2
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x0009
+pp_data->cap[0]->ReportID = 0x00
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 1
+pp_data->cap[0]->ReportCount = 8
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 8
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0002
+pp_data->cap[0]->LinkCollection = 0x0001
+pp_data->cap[0]->LinkUsagePage = 0x0001
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 1
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->Range.UsageMin = 0x0001
+pp_data->cap[0]->Range.UsageMax = 0x0003
+pp_data->cap[0]->Range.StringMin = 0
+pp_data->cap[0]->Range.StringMax = 0
+pp_data->cap[0]->Range.DesignatorMin = 0
+pp_data->cap[0]->Range.DesignatorMax = 0
+pp_data->cap[0]->Range.DataIndexMin = 0
+pp_data->cap[0]->Range.DataIndexMax = 2
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x0001
+pp_data->cap[1]->ReportID = 0x00
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 8
+pp_data->cap[1]->ReportCount = 1
+pp_data->cap[1]->BytePosition = 0x0004
+pp_data->cap[1]->BitCount = 8
+pp_data->cap[1]->BitField = 0x06
+pp_data->cap[1]->NextBytePosition = 0x0005
+pp_data->cap[1]->LinkCollection = 0x0001
+pp_data->cap[1]->LinkUsagePage = 0x0001
+pp_data->cap[1]->LinkUsage = 0x0001
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 0
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 0
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0038
+pp_data->cap[1]->NotRange.Reserved1 = 0x0038
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 3
+pp_data->cap[1]->NotRange.Reserved4 = 3
+pp_data->cap[1]->NotButton.HasNull = 0
+pp_data->cap[1]->NotButton.Reserved4 = 0x000000
+pp_data->cap[1]->NotButton.LogicalMin = -127
+pp_data->cap[1]->NotButton.LogicalMax = 127
+pp_data->cap[1]->NotButton.PhysicalMin = 0
+pp_data->cap[1]->NotButton.PhysicalMax = 0
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+pp_data->cap[2]->UsagePage = 0x0001
+pp_data->cap[2]->ReportID = 0x00
+pp_data->cap[2]->BitPosition = 0
+pp_data->cap[2]->BitSize = 8
+pp_data->cap[2]->ReportCount = 1
+pp_data->cap[2]->BytePosition = 0x0003
+pp_data->cap[2]->BitCount = 8
+pp_data->cap[2]->BitField = 0x06
+pp_data->cap[2]->NextBytePosition = 0x0004
+pp_data->cap[2]->LinkCollection = 0x0001
+pp_data->cap[2]->LinkUsagePage = 0x0001
+pp_data->cap[2]->LinkUsage = 0x0001
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 0
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 0
+pp_data->cap[2]->IsRange = 0
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->NotRange.Usage = 0x0031
+pp_data->cap[2]->NotRange.Reserved1 = 0x0031
+pp_data->cap[2]->NotRange.StringIndex = 0
+pp_data->cap[2]->NotRange.Reserved2 = 0
+pp_data->cap[2]->NotRange.DesignatorIndex = 0
+pp_data->cap[2]->NotRange.Reserved3 = 0
+pp_data->cap[2]->NotRange.DataIndex = 4
+pp_data->cap[2]->NotRange.Reserved4 = 4
+pp_data->cap[2]->NotButton.HasNull = 0
+pp_data->cap[2]->NotButton.Reserved4 = 0x000000
+pp_data->cap[2]->NotButton.LogicalMin = -127
+pp_data->cap[2]->NotButton.LogicalMax = 127
+pp_data->cap[2]->NotButton.PhysicalMin = 0
+pp_data->cap[2]->NotButton.PhysicalMax = 0
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+pp_data->cap[3]->UsagePage = 0x0001
+pp_data->cap[3]->ReportID = 0x00
+pp_data->cap[3]->BitPosition = 0
+pp_data->cap[3]->BitSize = 8
+pp_data->cap[3]->ReportCount = 1
+pp_data->cap[3]->BytePosition = 0x0002
+pp_data->cap[3]->BitCount = 8
+pp_data->cap[3]->BitField = 0x06
+pp_data->cap[3]->NextBytePosition = 0x0003
+pp_data->cap[3]->LinkCollection = 0x0001
+pp_data->cap[3]->LinkUsagePage = 0x0001
+pp_data->cap[3]->LinkUsage = 0x0001
+pp_data->cap[3]->IsMultipleItemsForArray = 0
+pp_data->cap[3]->IsButtonCap = 0
+pp_data->cap[3]->IsPadding = 0
+pp_data->cap[3]->IsAbsolute = 0
+pp_data->cap[3]->IsRange = 0
+pp_data->cap[3]->IsAlias = 0
+pp_data->cap[3]->IsStringRange = 0
+pp_data->cap[3]->IsDesignatorRange = 0
+pp_data->cap[3]->Reserved1 = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[3]->NotRange.Usage = 0x0030
+pp_data->cap[3]->NotRange.Reserved1 = 0x0030
+pp_data->cap[3]->NotRange.StringIndex = 0
+pp_data->cap[3]->NotRange.Reserved2 = 0
+pp_data->cap[3]->NotRange.DesignatorIndex = 0
+pp_data->cap[3]->NotRange.Reserved3 = 0
+pp_data->cap[3]->NotRange.DataIndex = 5
+pp_data->cap[3]->NotRange.Reserved4 = 5
+pp_data->cap[3]->NotButton.HasNull = 0
+pp_data->cap[3]->NotButton.Reserved4 = 0x000000
+pp_data->cap[3]->NotButton.LogicalMin = -127
+pp_data->cap[3]->NotButton.LogicalMax = 127
+pp_data->cap[3]->NotButton.PhysicalMin = 0
+pp_data->cap[3]->NotButton.PhysicalMax = 0
+pp_data->cap[3]->Units = 0
+pp_data->cap[3]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0002
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 1
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 1
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[1]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[1]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[1]->Parent = 0
+pp_data->LinkCollectionArray[1]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[1]->NextSibling = 0
+pp_data->LinkCollectionArray[1]->FirstChild = 0
+pp_data->LinkCollectionArray[1]->CollectionType = 0
+pp_data->LinkCollectionArray[1]->IsAlias = 0
+pp_data->LinkCollectionArray[1]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_C077_0002_0001_expected.rpt_desc b/src/hidapi/windows/test/data/046D_C077_0002_0001_expected.rpt_desc
new file mode 100644
index 0000000..7e144b4
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C077_0002_0001_expected.rpt_desc
@@ -0,0 +1,5 @@
+0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, 0x00,
+0x05, 0x09, 0x19, 0x01, 0x29, 0x03, 0x15, 0x00, 0x25, 0x01,
+0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x05, 0x01, 0x09, 0x30,
+0x09, 0x31, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08,
+0x95, 0x03, 0x81, 0x06, 0xC0, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C077_0002_0001_real.rpt_desc b/src/hidapi/windows/test/data/046D_C077_0002_0001_real.rpt_desc
new file mode 100644
index 0000000..7604c55
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C077_0002_0001_real.rpt_desc
@@ -0,0 +1,24 @@
+Usage Page (Generic Desktop) 05 01
+Usage (Mouse) 09 02
+Collection (Application) A1 01
+ Usage (Pointer) 09 01
+ Collection (Physical) A1 00
+ Usage Page (Button) 05 09
+ Usage Minimum (Button 1) 19 01
+ Usage Maximum (Button 3) 29 03
+ Logical Minimum (0) 15 00
+ Logical Maximum (1) 25 01
+ Report Count (8) 95 08
+ Report Size (1) 75 01
+ Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
+ Usage Page (Generic Desktop) 05 01
+ Usage (X) 09 30
+ Usage (Y) 09 31
+ Usage (Wheel) 09 38
+ Logical Minimum (-127) 15 81
+ Logical Maximum (127) 25 7F
+ Report Size (8) 75 08
+ Report Count (3) 95 03
+ Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit) 81 06
+ End Collection C0
+End Collection C0
diff --git a/src/hidapi/windows/test/data/046D_C283_0004_0001.pp_data b/src/hidapi/windows/test/data/046D_C283_0004_0001.pp_data
new file mode 100644
index 0000000..0f70d06
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C283_0004_0001.pp_data
@@ -0,0 +1,520 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xC283
+dev->manufacturer_string = "Logitech Inc."
+dev->product_string = "WingMan Force 3D"
+dev->release_number = 0x0106
+dev->interface_number = -1
+dev->usage = 0x0004
+dev->usage_page = 0x0001
+dev->path = "\\?\hid#vid_046d&pid_c283#7&d7fb4bf&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0004
+pp_data->UsagePage = 0x0001
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 8
+pp_data->caps_info[0]->NumberOfCaps = 8
+pp_data->caps_info[0]->ReportByteLength = 8
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 8
+pp_data->caps_info[1]->LastCap = 9
+pp_data->caps_info[1]->NumberOfCaps = 1
+pp_data->caps_info[1]->ReportByteLength = 9
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 9
+pp_data->caps_info[2]->LastCap = 9
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x03A8
+pp_data->NumberLinkCollectionNodes = 4
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x0001
+pp_data->cap[0]->ReportID = 0x00
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 8
+pp_data->cap[0]->ReportCount = 1
+pp_data->cap[0]->BytePosition = 0x0002
+pp_data->cap[0]->BitCount = 8
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0003
+pp_data->cap[0]->LinkCollection = 0x0002
+pp_data->cap[0]->LinkUsagePage = 0x0001
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 0
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0031
+pp_data->cap[0]->NotRange.Reserved1 = 0x0031
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->NotButton.HasNull = 0
+pp_data->cap[0]->NotButton.Reserved4 = 0x000000
+pp_data->cap[0]->NotButton.LogicalMin = 0
+pp_data->cap[0]->NotButton.LogicalMax = 255
+pp_data->cap[0]->NotButton.PhysicalMin = 0
+pp_data->cap[0]->NotButton.PhysicalMax = 255
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x0001
+pp_data->cap[1]->ReportID = 0x00
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 8
+pp_data->cap[1]->ReportCount = 1
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 8
+pp_data->cap[1]->BitField = 0x02
+pp_data->cap[1]->NextBytePosition = 0x0002
+pp_data->cap[1]->LinkCollection = 0x0002
+pp_data->cap[1]->LinkUsagePage = 0x0001
+pp_data->cap[1]->LinkUsage = 0x0001
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 0
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0030
+pp_data->cap[1]->NotRange.Reserved1 = 0x0030
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 1
+pp_data->cap[1]->NotRange.Reserved4 = 1
+pp_data->cap[1]->NotButton.HasNull = 0
+pp_data->cap[1]->NotButton.Reserved4 = 0x000000
+pp_data->cap[1]->NotButton.LogicalMin = 0
+pp_data->cap[1]->NotButton.LogicalMax = 255
+pp_data->cap[1]->NotButton.PhysicalMin = 0
+pp_data->cap[1]->NotButton.PhysicalMax = 255
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+pp_data->cap[2]->UsagePage = 0xFF00
+pp_data->cap[2]->ReportID = 0x00
+pp_data->cap[2]->BitPosition = 0
+pp_data->cap[2]->BitSize = 4
+pp_data->cap[2]->ReportCount = 1
+pp_data->cap[2]->BytePosition = 0x0003
+pp_data->cap[2]->BitCount = 4
+pp_data->cap[2]->BitField = 0x02
+pp_data->cap[2]->NextBytePosition = 0x0004
+pp_data->cap[2]->LinkCollection = 0x0002
+pp_data->cap[2]->LinkUsagePage = 0x0001
+pp_data->cap[2]->LinkUsage = 0x0001
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 0
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 1
+pp_data->cap[2]->IsRange = 0
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->NotRange.Usage = 0x0001
+pp_data->cap[2]->NotRange.Reserved1 = 0x0001
+pp_data->cap[2]->NotRange.StringIndex = 0
+pp_data->cap[2]->NotRange.Reserved2 = 0
+pp_data->cap[2]->NotRange.DesignatorIndex = 0
+pp_data->cap[2]->NotRange.Reserved3 = 0
+pp_data->cap[2]->NotRange.DataIndex = 2
+pp_data->cap[2]->NotRange.Reserved4 = 2
+pp_data->cap[2]->NotButton.HasNull = 0
+pp_data->cap[2]->NotButton.Reserved4 = 0x000000
+pp_data->cap[2]->NotButton.LogicalMin = 0
+pp_data->cap[2]->NotButton.LogicalMax = 15
+pp_data->cap[2]->NotButton.PhysicalMin = 0
+pp_data->cap[2]->NotButton.PhysicalMax = 255
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+pp_data->cap[3]->UsagePage = 0x0001
+pp_data->cap[3]->ReportID = 0x00
+pp_data->cap[3]->BitPosition = 4
+pp_data->cap[3]->BitSize = 4
+pp_data->cap[3]->ReportCount = 1
+pp_data->cap[3]->BytePosition = 0x0003
+pp_data->cap[3]->BitCount = 4
+pp_data->cap[3]->BitField = 0x42
+pp_data->cap[3]->NextBytePosition = 0x0004
+pp_data->cap[3]->LinkCollection = 0x0002
+pp_data->cap[3]->LinkUsagePage = 0x0001
+pp_data->cap[3]->LinkUsage = 0x0001
+pp_data->cap[3]->IsMultipleItemsForArray = 0
+pp_data->cap[3]->IsButtonCap = 0
+pp_data->cap[3]->IsPadding = 0
+pp_data->cap[3]->IsAbsolute = 1
+pp_data->cap[3]->IsRange = 0
+pp_data->cap[3]->IsAlias = 0
+pp_data->cap[3]->IsStringRange = 0
+pp_data->cap[3]->IsDesignatorRange = 0
+pp_data->cap[3]->Reserved1 = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[3]->NotRange.Usage = 0x0039
+pp_data->cap[3]->NotRange.Reserved1 = 0x0039
+pp_data->cap[3]->NotRange.StringIndex = 0
+pp_data->cap[3]->NotRange.Reserved2 = 0
+pp_data->cap[3]->NotRange.DesignatorIndex = 0
+pp_data->cap[3]->NotRange.Reserved3 = 0
+pp_data->cap[3]->NotRange.DataIndex = 3
+pp_data->cap[3]->NotRange.Reserved4 = 3
+pp_data->cap[3]->NotButton.HasNull = 1
+pp_data->cap[3]->NotButton.Reserved4 = 0x000000
+pp_data->cap[3]->NotButton.LogicalMin = 0
+pp_data->cap[3]->NotButton.LogicalMax = 7
+pp_data->cap[3]->NotButton.PhysicalMin = 0
+pp_data->cap[3]->NotButton.PhysicalMax = 315
+pp_data->cap[3]->Units = 20
+pp_data->cap[3]->UnitsExp = 0
+
+pp_data->cap[4]->UsagePage = 0x0001
+pp_data->cap[4]->ReportID = 0x00
+pp_data->cap[4]->BitPosition = 0
+pp_data->cap[4]->BitSize = 8
+pp_data->cap[4]->ReportCount = 1
+pp_data->cap[4]->BytePosition = 0x0004
+pp_data->cap[4]->BitCount = 8
+pp_data->cap[4]->BitField = 0x02
+pp_data->cap[4]->NextBytePosition = 0x0005
+pp_data->cap[4]->LinkCollection = 0x0002
+pp_data->cap[4]->LinkUsagePage = 0x0001
+pp_data->cap[4]->LinkUsage = 0x0001
+pp_data->cap[4]->IsMultipleItemsForArray = 0
+pp_data->cap[4]->IsButtonCap = 0
+pp_data->cap[4]->IsPadding = 0
+pp_data->cap[4]->IsAbsolute = 1
+pp_data->cap[4]->IsRange = 0
+pp_data->cap[4]->IsAlias = 0
+pp_data->cap[4]->IsStringRange = 0
+pp_data->cap[4]->IsDesignatorRange = 0
+pp_data->cap[4]->Reserved1 = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[4]->NotRange.Usage = 0x0035
+pp_data->cap[4]->NotRange.Reserved1 = 0x0035
+pp_data->cap[4]->NotRange.StringIndex = 0
+pp_data->cap[4]->NotRange.Reserved2 = 0
+pp_data->cap[4]->NotRange.DesignatorIndex = 0
+pp_data->cap[4]->NotRange.Reserved3 = 0
+pp_data->cap[4]->NotRange.DataIndex = 4
+pp_data->cap[4]->NotRange.Reserved4 = 4
+pp_data->cap[4]->NotButton.HasNull = 0
+pp_data->cap[4]->NotButton.Reserved4 = 0x000000
+pp_data->cap[4]->NotButton.LogicalMin = 0
+pp_data->cap[4]->NotButton.LogicalMax = 255
+pp_data->cap[4]->NotButton.PhysicalMin = 0
+pp_data->cap[4]->NotButton.PhysicalMax = 255
+pp_data->cap[4]->Units = 20
+pp_data->cap[4]->UnitsExp = 0
+
+pp_data->cap[5]->UsagePage = 0x0009
+pp_data->cap[5]->ReportID = 0x00
+pp_data->cap[5]->BitPosition = 0
+pp_data->cap[5]->BitSize = 1
+pp_data->cap[5]->ReportCount = 7
+pp_data->cap[5]->BytePosition = 0x0005
+pp_data->cap[5]->BitCount = 7
+pp_data->cap[5]->BitField = 0x02
+pp_data->cap[5]->NextBytePosition = 0x0006
+pp_data->cap[5]->LinkCollection = 0x0001
+pp_data->cap[5]->LinkUsagePage = 0x0001
+pp_data->cap[5]->LinkUsage = 0x0000
+pp_data->cap[5]->IsMultipleItemsForArray = 0
+pp_data->cap[5]->IsButtonCap = 1
+pp_data->cap[5]->IsPadding = 0
+pp_data->cap[5]->IsAbsolute = 1
+pp_data->cap[5]->IsRange = 1
+pp_data->cap[5]->IsAlias = 0
+pp_data->cap[5]->IsStringRange = 0
+pp_data->cap[5]->IsDesignatorRange = 0
+pp_data->cap[5]->Reserved1 = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[5]->Range.UsageMin = 0x0001
+pp_data->cap[5]->Range.UsageMax = 0x0007
+pp_data->cap[5]->Range.StringMin = 0
+pp_data->cap[5]->Range.StringMax = 0
+pp_data->cap[5]->Range.DesignatorMin = 0
+pp_data->cap[5]->Range.DesignatorMax = 0
+pp_data->cap[5]->Range.DataIndexMin = 5
+pp_data->cap[5]->Range.DataIndexMax = 11
+pp_data->cap[5]->Button.LogicalMin = 0
+pp_data->cap[5]->Button.LogicalMax = 0
+pp_data->cap[5]->Units = 0
+pp_data->cap[5]->UnitsExp = 0
+
+pp_data->cap[6]->UsagePage = 0x0001
+pp_data->cap[6]->ReportID = 0x00
+pp_data->cap[6]->BitPosition = 0
+pp_data->cap[6]->BitSize = 8
+pp_data->cap[6]->ReportCount = 1
+pp_data->cap[6]->BytePosition = 0x0006
+pp_data->cap[6]->BitCount = 8
+pp_data->cap[6]->BitField = 0x02
+pp_data->cap[6]->NextBytePosition = 0x0007
+pp_data->cap[6]->LinkCollection = 0x0001
+pp_data->cap[6]->LinkUsagePage = 0x0001
+pp_data->cap[6]->LinkUsage = 0x0000
+pp_data->cap[6]->IsMultipleItemsForArray = 0
+pp_data->cap[6]->IsButtonCap = 0
+pp_data->cap[6]->IsPadding = 0
+pp_data->cap[6]->IsAbsolute = 1
+pp_data->cap[6]->IsRange = 0
+pp_data->cap[6]->IsAlias = 0
+pp_data->cap[6]->IsStringRange = 0
+pp_data->cap[6]->IsDesignatorRange = 0
+pp_data->cap[6]->Reserved1 = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[6]->NotRange.Usage = 0x0036
+pp_data->cap[6]->NotRange.Reserved1 = 0x0036
+pp_data->cap[6]->NotRange.StringIndex = 0
+pp_data->cap[6]->NotRange.Reserved2 = 0
+pp_data->cap[6]->NotRange.DesignatorIndex = 0
+pp_data->cap[6]->NotRange.Reserved3 = 0
+pp_data->cap[6]->NotRange.DataIndex = 12
+pp_data->cap[6]->NotRange.Reserved4 = 12
+pp_data->cap[6]->NotButton.HasNull = 0
+pp_data->cap[6]->NotButton.Reserved4 = 0x000000
+pp_data->cap[6]->NotButton.LogicalMin = 0
+pp_data->cap[6]->NotButton.LogicalMax = 255
+pp_data->cap[6]->NotButton.PhysicalMin = 0
+pp_data->cap[6]->NotButton.PhysicalMax = 255
+pp_data->cap[6]->Units = 0
+pp_data->cap[6]->UnitsExp = 0
+
+pp_data->cap[7]->UsagePage = 0xFF00
+pp_data->cap[7]->ReportID = 0x00
+pp_data->cap[7]->BitPosition = 0
+pp_data->cap[7]->BitSize = 8
+pp_data->cap[7]->ReportCount = 1
+pp_data->cap[7]->BytePosition = 0x0007
+pp_data->cap[7]->BitCount = 8
+pp_data->cap[7]->BitField = 0x02
+pp_data->cap[7]->NextBytePosition = 0x0008
+pp_data->cap[7]->LinkCollection = 0x0001
+pp_data->cap[7]->LinkUsagePage = 0x0001
+pp_data->cap[7]->LinkUsage = 0x0000
+pp_data->cap[7]->IsMultipleItemsForArray = 0
+pp_data->cap[7]->IsButtonCap = 0
+pp_data->cap[7]->IsPadding = 0
+pp_data->cap[7]->IsAbsolute = 1
+pp_data->cap[7]->IsRange = 0
+pp_data->cap[7]->IsAlias = 0
+pp_data->cap[7]->IsStringRange = 0
+pp_data->cap[7]->IsDesignatorRange = 0
+pp_data->cap[7]->Reserved1 = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[7]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[7]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[7]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[7]->NotRange.Usage = 0x0001
+pp_data->cap[7]->NotRange.Reserved1 = 0x0001
+pp_data->cap[7]->NotRange.StringIndex = 0
+pp_data->cap[7]->NotRange.Reserved2 = 0
+pp_data->cap[7]->NotRange.DesignatorIndex = 0
+pp_data->cap[7]->NotRange.Reserved3 = 0
+pp_data->cap[7]->NotRange.DataIndex = 13
+pp_data->cap[7]->NotRange.Reserved4 = 13
+pp_data->cap[7]->NotButton.HasNull = 0
+pp_data->cap[7]->NotButton.Reserved4 = 0x000000
+pp_data->cap[7]->NotButton.LogicalMin = 0
+pp_data->cap[7]->NotButton.LogicalMax = 255
+pp_data->cap[7]->NotButton.PhysicalMin = 0
+pp_data->cap[7]->NotButton.PhysicalMax = 255
+pp_data->cap[7]->Units = 0
+pp_data->cap[7]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[8]->UsagePage = 0xFF00
+pp_data->cap[8]->ReportID = 0x00
+pp_data->cap[8]->BitPosition = 0
+pp_data->cap[8]->BitSize = 8
+pp_data->cap[8]->ReportCount = 8
+pp_data->cap[8]->BytePosition = 0x0001
+pp_data->cap[8]->BitCount = 64
+pp_data->cap[8]->BitField = 0x02
+pp_data->cap[8]->NextBytePosition = 0x0009
+pp_data->cap[8]->LinkCollection = 0x0003
+pp_data->cap[8]->LinkUsagePage = 0xFF00
+pp_data->cap[8]->LinkUsage = 0x0000
+pp_data->cap[8]->IsMultipleItemsForArray = 0
+pp_data->cap[8]->IsButtonCap = 0
+pp_data->cap[8]->IsPadding = 0
+pp_data->cap[8]->IsAbsolute = 1
+pp_data->cap[8]->IsRange = 0
+pp_data->cap[8]->IsAlias = 0
+pp_data->cap[8]->IsStringRange = 0
+pp_data->cap[8]->IsDesignatorRange = 0
+pp_data->cap[8]->Reserved1 = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[8]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[8]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[8]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[8]->NotRange.Usage = 0x0002
+pp_data->cap[8]->NotRange.Reserved1 = 0x0002
+pp_data->cap[8]->NotRange.StringIndex = 0
+pp_data->cap[8]->NotRange.Reserved2 = 0
+pp_data->cap[8]->NotRange.DesignatorIndex = 0
+pp_data->cap[8]->NotRange.Reserved3 = 0
+pp_data->cap[8]->NotRange.DataIndex = 0
+pp_data->cap[8]->NotRange.Reserved4 = 0
+pp_data->cap[8]->NotButton.HasNull = 0
+pp_data->cap[8]->NotButton.Reserved4 = 0x000000
+pp_data->cap[8]->NotButton.LogicalMin = 0
+pp_data->cap[8]->NotButton.LogicalMax = 255
+pp_data->cap[8]->NotButton.PhysicalMin = 0
+pp_data->cap[8]->NotButton.PhysicalMax = 255
+pp_data->cap[8]->Units = 0
+pp_data->cap[8]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0004
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 2
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 3
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[1]->LinkUsage = 0x0000
+pp_data->LinkCollectionArray[1]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[1]->Parent = 0
+pp_data->LinkCollectionArray[1]->NumberOfChildren = 1
+pp_data->LinkCollectionArray[1]->NextSibling = 0
+pp_data->LinkCollectionArray[1]->FirstChild = 2
+pp_data->LinkCollectionArray[1]->CollectionType = 2
+pp_data->LinkCollectionArray[1]->IsAlias = 0
+pp_data->LinkCollectionArray[1]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[2]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[2]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[2]->Parent = 1
+pp_data->LinkCollectionArray[2]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[2]->NextSibling = 0
+pp_data->LinkCollectionArray[2]->FirstChild = 0
+pp_data->LinkCollectionArray[2]->CollectionType = 0
+pp_data->LinkCollectionArray[2]->IsAlias = 0
+pp_data->LinkCollectionArray[2]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[3]->LinkUsage = 0x0000
+pp_data->LinkCollectionArray[3]->LinkUsagePage = 0xFF00
+pp_data->LinkCollectionArray[3]->Parent = 0
+pp_data->LinkCollectionArray[3]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[3]->NextSibling = 1
+pp_data->LinkCollectionArray[3]->FirstChild = 0
+pp_data->LinkCollectionArray[3]->CollectionType = 2
+pp_data->LinkCollectionArray[3]->IsAlias = 0
+pp_data->LinkCollectionArray[3]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_C283_0004_0001_expected.rpt_desc b/src/hidapi/windows/test/data/046D_C283_0004_0001_expected.rpt_desc
new file mode 100644
index 0000000..fca719a
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C283_0004_0001_expected.rpt_desc
@@ -0,0 +1,18 @@
+0x05, 0x01, 0x09, 0x04, 0xA1, 0x01, 0x09, 0x00, 0xA1, 0x02,
+0x09, 0x01, 0xA1, 0x00, 0x09, 0x30, 0x09, 0x31, 0x15, 0x00,
+0x26, 0xFF, 0x00, 0x35, 0x00, 0x46, 0xFF, 0x00, 0x75, 0x08,
+0x95, 0x02, 0x81, 0x02, 0x06, 0x00, 0xFF, 0x09, 0x01, 0x15,
+0x00, 0x25, 0x0F, 0x75, 0x04, 0x95, 0x01, 0x81, 0x02, 0x05,
+0x01, 0x09, 0x39, 0x15, 0x00, 0x25, 0x07, 0x35, 0x00, 0x46,
+0x3B, 0x01, 0x65, 0x14, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42,
+0x09, 0x35, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x35, 0x00, 0x46,
+0xFF, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0xC0, 0x05,
+0x09, 0x19, 0x01, 0x29, 0x07, 0x15, 0x00, 0x25, 0x01, 0x75,
+0x01, 0x95, 0x07, 0x45, 0x00, 0x65, 0x00, 0x81, 0x02, 0x75,
+0x01, 0x95, 0x01, 0x81, 0x03, 0x05, 0x01, 0x09, 0x36, 0x15,
+0x00, 0x26, 0xFF, 0x00, 0x35, 0x00, 0x46, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x01, 0x81, 0x02, 0x06, 0x00, 0xFF, 0x09, 0x01,
+0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81,
+0x02, 0xC0, 0x09, 0x00, 0xA1, 0x02, 0x09, 0x02, 0x15, 0x00,
+0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x08, 0x91, 0x02, 0xC0,
+0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C283_0004_0001_real.rpt_desc b/src/hidapi/windows/test/data/046D_C283_0004_0001_real.rpt_desc
new file mode 100644
index 0000000..fca719a
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C283_0004_0001_real.rpt_desc
@@ -0,0 +1,18 @@
+0x05, 0x01, 0x09, 0x04, 0xA1, 0x01, 0x09, 0x00, 0xA1, 0x02,
+0x09, 0x01, 0xA1, 0x00, 0x09, 0x30, 0x09, 0x31, 0x15, 0x00,
+0x26, 0xFF, 0x00, 0x35, 0x00, 0x46, 0xFF, 0x00, 0x75, 0x08,
+0x95, 0x02, 0x81, 0x02, 0x06, 0x00, 0xFF, 0x09, 0x01, 0x15,
+0x00, 0x25, 0x0F, 0x75, 0x04, 0x95, 0x01, 0x81, 0x02, 0x05,
+0x01, 0x09, 0x39, 0x15, 0x00, 0x25, 0x07, 0x35, 0x00, 0x46,
+0x3B, 0x01, 0x65, 0x14, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42,
+0x09, 0x35, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x35, 0x00, 0x46,
+0xFF, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0xC0, 0x05,
+0x09, 0x19, 0x01, 0x29, 0x07, 0x15, 0x00, 0x25, 0x01, 0x75,
+0x01, 0x95, 0x07, 0x45, 0x00, 0x65, 0x00, 0x81, 0x02, 0x75,
+0x01, 0x95, 0x01, 0x81, 0x03, 0x05, 0x01, 0x09, 0x36, 0x15,
+0x00, 0x26, 0xFF, 0x00, 0x35, 0x00, 0x46, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x01, 0x81, 0x02, 0x06, 0x00, 0xFF, 0x09, 0x01,
+0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81,
+0x02, 0xC0, 0x09, 0x00, 0xA1, 0x02, 0x09, 0x02, 0x15, 0x00,
+0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x08, 0x91, 0x02, 0xC0,
+0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C52F_0001_000C.pp_data b/src/hidapi/windows/test/data/046D_C52F_0001_000C.pp_data
new file mode 100644
index 0000000..7f6b369
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C52F_0001_000C.pp_data
@@ -0,0 +1,93 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xC52F
+dev->manufacturer_string = "Logitech"
+dev->product_string = "USB Receiver"
+dev->release_number = 0x2200
+dev->interface_number = 1
+dev->usage = 0x0001
+dev->usage_page = 0x000C
+dev->path = "\\?\hid#vid_046d&pid_c52f&mi_01&col01#8&28ca146b&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0001
+pp_data->UsagePage = 0x000C
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 1
+pp_data->caps_info[0]->NumberOfCaps = 1
+pp_data->caps_info[0]->ReportByteLength = 5
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 1
+pp_data->caps_info[1]->LastCap = 1
+pp_data->caps_info[1]->NumberOfCaps = 0
+pp_data->caps_info[1]->ReportByteLength = 0
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 1
+pp_data->caps_info[2]->LastCap = 1
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x0068
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x000C
+pp_data->cap[0]->ReportID = 0x03
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 16
+pp_data->cap[0]->ReportCount = 2
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 32
+pp_data->cap[0]->BitField = 0x00
+pp_data->cap[0]->NextBytePosition = 0x0005
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0x000C
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 1
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->Range.UsageMin = 0x0001
+pp_data->cap[0]->Range.UsageMax = 0x028C
+pp_data->cap[0]->Range.StringMin = 0
+pp_data->cap[0]->Range.StringMax = 0
+pp_data->cap[0]->Range.DesignatorMin = 0
+pp_data->cap[0]->Range.DesignatorMax = 0
+pp_data->cap[0]->Range.DataIndexMin = 0
+pp_data->cap[0]->Range.DataIndexMax = 651
+pp_data->cap[0]->Button.LogicalMin = 1
+pp_data->cap[0]->Button.LogicalMax = 652
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x000C
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_C52F_0001_000C_expected.rpt_desc b/src/hidapi/windows/test/data/046D_C52F_0001_000C_expected.rpt_desc
new file mode 100644
index 0000000..85953ae
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C52F_0001_000C_expected.rpt_desc
@@ -0,0 +1,3 @@
+0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x03, 0x19, 0x01,
+0x2A, 0x8C, 0x02, 0x15, 0x01, 0x26, 0x8C, 0x02, 0x75, 0x10,
+0x95, 0x02, 0x81, 0x00, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C52F_0001_000C_real.rpt_desc b/src/hidapi/windows/test/data/046D_C52F_0001_000C_real.rpt_desc
new file mode 100644
index 0000000..280e58f
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C52F_0001_000C_real.rpt_desc
@@ -0,0 +1,12 @@
+Usage Page (Consumer Devices) 05 0C
+Usage (Consumer Control) 09 01
+Collection (Application) A1 01
+ Report ID (3) 85 03
+ Report Size (16) 75 10
+ Report Count (2) 95 02
+ Logical Minimum (1) 15 01
+ Logical Maximum (652) 26 8C 02
+ Usage Minimum (Consumer Control) 19 01
+ Usage Maximum (AC Send) 2A 8C 02
+ Input (Data,Ary,Abs) 81 00
+End Collection C0
diff --git a/src/hidapi/windows/test/data/046D_C52F_0001_FF00.pp_data b/src/hidapi/windows/test/data/046D_C52F_0001_FF00.pp_data
new file mode 100644
index 0000000..5e8ece1
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C52F_0001_FF00.pp_data
@@ -0,0 +1,139 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xC52F
+dev->manufacturer_string = "Logitech"
+dev->product_string = "USB Receiver"
+dev->release_number = 0x2200
+dev->interface_number = 1
+dev->usage = 0x0001
+dev->usage_page = 0xFF00
+dev->path = "\\?\hid#vid_046d&pid_c52f&mi_01&col02#8&28ca146b&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0001
+pp_data->UsagePage = 0xFF00
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 1
+pp_data->caps_info[0]->NumberOfCaps = 1
+pp_data->caps_info[0]->ReportByteLength = 7
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 1
+pp_data->caps_info[1]->LastCap = 2
+pp_data->caps_info[1]->NumberOfCaps = 1
+pp_data->caps_info[1]->ReportByteLength = 7
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 2
+pp_data->caps_info[2]->LastCap = 2
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x00D0
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0xFF00
+pp_data->cap[0]->ReportID = 0x10
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 8
+pp_data->cap[0]->ReportCount = 6
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 48
+pp_data->cap[0]->BitField = 0x00
+pp_data->cap[0]->NextBytePosition = 0x0007
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0xFF00
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0001
+pp_data->cap[0]->NotRange.Reserved1 = 0x0001
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 255
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[1]->UsagePage = 0xFF00
+pp_data->cap[1]->ReportID = 0x10
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 8
+pp_data->cap[1]->ReportCount = 6
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 48
+pp_data->cap[1]->BitField = 0x00
+pp_data->cap[1]->NextBytePosition = 0x0007
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0xFF00
+pp_data->cap[1]->LinkUsage = 0x0001
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0001
+pp_data->cap[1]->NotRange.Reserved1 = 0x0001
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 0
+pp_data->cap[1]->NotRange.Reserved4 = 0
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 255
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0xFF00
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_C52F_0001_FF00_expected.rpt_desc b/src/hidapi/windows/test/data/046D_C52F_0001_FF00_expected.rpt_desc
new file mode 100644
index 0000000..812bd2a
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C52F_0001_FF00_expected.rpt_desc
@@ -0,0 +1,4 @@
+0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x10, 0x09,
+0x01, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x06,
+0x81, 0x00, 0x09, 0x01, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x06, 0x91, 0x00, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C52F_0001_FF00_real.rpt_desc b/src/hidapi/windows/test/data/046D_C52F_0001_FF00_real.rpt_desc
new file mode 100644
index 0000000..0db6898
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C52F_0001_FF00_real.rpt_desc
@@ -0,0 +1,13 @@
+Usage Page (Vendor-Defined 1) 06 00 FF
+Usage (Vendor-Defined 1) 09 01
+Collection (Application) A1 01
+ Report ID (16) 85 10
+ Report Size (8) 75 08
+ Report Count (6) 95 06
+ Logical Minimum (0) 15 00
+ Logical Maximum (255) 26 FF 00
+ Usage (Vendor-Defined 1) 09 01
+ Input (Data,Ary,Abs) 81 00
+ Usage (Vendor-Defined 1) 09 01
+ Output (Data,Ary,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 00
+End Collection C0
diff --git a/src/hidapi/windows/test/data/046D_C52F_0002_0001.pp_data b/src/hidapi/windows/test/data/046D_C52F_0002_0001.pp_data
new file mode 100644
index 0000000..d90e666
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C52F_0002_0001.pp_data
@@ -0,0 +1,302 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xC52F
+dev->manufacturer_string = "Logitech"
+dev->product_string = "USB Receiver"
+dev->release_number = 0x2200
+dev->interface_number = 0
+dev->usage = 0x0002
+dev->usage_page = 0x0001
+dev->path = "\\?\hid#vid_046d&pid_c52f&mi_00#8&1599f82d&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0002
+pp_data->UsagePage = 0x0001
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 5
+pp_data->caps_info[0]->NumberOfCaps = 5
+pp_data->caps_info[0]->ReportByteLength = 9
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 5
+pp_data->caps_info[1]->LastCap = 5
+pp_data->caps_info[1]->NumberOfCaps = 0
+pp_data->caps_info[1]->ReportByteLength = 0
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 5
+pp_data->caps_info[2]->LastCap = 5
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x0208
+pp_data->NumberLinkCollectionNodes = 2
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x0009
+pp_data->cap[0]->ReportID = 0x00
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 1
+pp_data->cap[0]->ReportCount = 16
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 16
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0003
+pp_data->cap[0]->LinkCollection = 0x0001
+pp_data->cap[0]->LinkUsagePage = 0x0001
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 1
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->Range.UsageMin = 0x0001
+pp_data->cap[0]->Range.UsageMax = 0x0010
+pp_data->cap[0]->Range.StringMin = 0
+pp_data->cap[0]->Range.StringMax = 0
+pp_data->cap[0]->Range.DesignatorMin = 0
+pp_data->cap[0]->Range.DesignatorMax = 0
+pp_data->cap[0]->Range.DataIndexMin = 0
+pp_data->cap[0]->Range.DataIndexMax = 15
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x0001
+pp_data->cap[1]->ReportID = 0x00
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 16
+pp_data->cap[1]->ReportCount = 1
+pp_data->cap[1]->BytePosition = 0x0005
+pp_data->cap[1]->BitCount = 16
+pp_data->cap[1]->BitField = 0x06
+pp_data->cap[1]->NextBytePosition = 0x0007
+pp_data->cap[1]->LinkCollection = 0x0001
+pp_data->cap[1]->LinkUsagePage = 0x0001
+pp_data->cap[1]->LinkUsage = 0x0001
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 0
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 0
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0031
+pp_data->cap[1]->NotRange.Reserved1 = 0x0031
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 16
+pp_data->cap[1]->NotRange.Reserved4 = 16
+pp_data->cap[1]->NotButton.HasNull = 0
+pp_data->cap[1]->NotButton.Reserved4 = 0x000000
+pp_data->cap[1]->NotButton.LogicalMin = -32767
+pp_data->cap[1]->NotButton.LogicalMax = 32767
+pp_data->cap[1]->NotButton.PhysicalMin = 0
+pp_data->cap[1]->NotButton.PhysicalMax = 0
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+pp_data->cap[2]->UsagePage = 0x0001
+pp_data->cap[2]->ReportID = 0x00
+pp_data->cap[2]->BitPosition = 0
+pp_data->cap[2]->BitSize = 16
+pp_data->cap[2]->ReportCount = 1
+pp_data->cap[2]->BytePosition = 0x0003
+pp_data->cap[2]->BitCount = 16
+pp_data->cap[2]->BitField = 0x06
+pp_data->cap[2]->NextBytePosition = 0x0005
+pp_data->cap[2]->LinkCollection = 0x0001
+pp_data->cap[2]->LinkUsagePage = 0x0001
+pp_data->cap[2]->LinkUsage = 0x0001
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 0
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 0
+pp_data->cap[2]->IsRange = 0
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->NotRange.Usage = 0x0030
+pp_data->cap[2]->NotRange.Reserved1 = 0x0030
+pp_data->cap[2]->NotRange.StringIndex = 0
+pp_data->cap[2]->NotRange.Reserved2 = 0
+pp_data->cap[2]->NotRange.DesignatorIndex = 0
+pp_data->cap[2]->NotRange.Reserved3 = 0
+pp_data->cap[2]->NotRange.DataIndex = 17
+pp_data->cap[2]->NotRange.Reserved4 = 17
+pp_data->cap[2]->NotButton.HasNull = 0
+pp_data->cap[2]->NotButton.Reserved4 = 0x000000
+pp_data->cap[2]->NotButton.LogicalMin = -32767
+pp_data->cap[2]->NotButton.LogicalMax = 32767
+pp_data->cap[2]->NotButton.PhysicalMin = 0
+pp_data->cap[2]->NotButton.PhysicalMax = 0
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+pp_data->cap[3]->UsagePage = 0x0001
+pp_data->cap[3]->ReportID = 0x00
+pp_data->cap[3]->BitPosition = 0
+pp_data->cap[3]->BitSize = 8
+pp_data->cap[3]->ReportCount = 1
+pp_data->cap[3]->BytePosition = 0x0007
+pp_data->cap[3]->BitCount = 8
+pp_data->cap[3]->BitField = 0x06
+pp_data->cap[3]->NextBytePosition = 0x0008
+pp_data->cap[3]->LinkCollection = 0x0001
+pp_data->cap[3]->LinkUsagePage = 0x0001
+pp_data->cap[3]->LinkUsage = 0x0001
+pp_data->cap[3]->IsMultipleItemsForArray = 0
+pp_data->cap[3]->IsButtonCap = 0
+pp_data->cap[3]->IsPadding = 0
+pp_data->cap[3]->IsAbsolute = 0
+pp_data->cap[3]->IsRange = 0
+pp_data->cap[3]->IsAlias = 0
+pp_data->cap[3]->IsStringRange = 0
+pp_data->cap[3]->IsDesignatorRange = 0
+pp_data->cap[3]->Reserved1 = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[3]->NotRange.Usage = 0x0038
+pp_data->cap[3]->NotRange.Reserved1 = 0x0038
+pp_data->cap[3]->NotRange.StringIndex = 0
+pp_data->cap[3]->NotRange.Reserved2 = 0
+pp_data->cap[3]->NotRange.DesignatorIndex = 0
+pp_data->cap[3]->NotRange.Reserved3 = 0
+pp_data->cap[3]->NotRange.DataIndex = 18
+pp_data->cap[3]->NotRange.Reserved4 = 18
+pp_data->cap[3]->NotButton.HasNull = 0
+pp_data->cap[3]->NotButton.Reserved4 = 0x000000
+pp_data->cap[3]->NotButton.LogicalMin = -127
+pp_data->cap[3]->NotButton.LogicalMax = 127
+pp_data->cap[3]->NotButton.PhysicalMin = 0
+pp_data->cap[3]->NotButton.PhysicalMax = 0
+pp_data->cap[3]->Units = 0
+pp_data->cap[3]->UnitsExp = 0
+
+pp_data->cap[4]->UsagePage = 0x000C
+pp_data->cap[4]->ReportID = 0x00
+pp_data->cap[4]->BitPosition = 0
+pp_data->cap[4]->BitSize = 8
+pp_data->cap[4]->ReportCount = 1
+pp_data->cap[4]->BytePosition = 0x0008
+pp_data->cap[4]->BitCount = 8
+pp_data->cap[4]->BitField = 0x06
+pp_data->cap[4]->NextBytePosition = 0x0009
+pp_data->cap[4]->LinkCollection = 0x0001
+pp_data->cap[4]->LinkUsagePage = 0x0001
+pp_data->cap[4]->LinkUsage = 0x0001
+pp_data->cap[4]->IsMultipleItemsForArray = 0
+pp_data->cap[4]->IsButtonCap = 0
+pp_data->cap[4]->IsPadding = 0
+pp_data->cap[4]->IsAbsolute = 0
+pp_data->cap[4]->IsRange = 0
+pp_data->cap[4]->IsAlias = 0
+pp_data->cap[4]->IsStringRange = 0
+pp_data->cap[4]->IsDesignatorRange = 0
+pp_data->cap[4]->Reserved1 = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[4]->NotRange.Usage = 0x0238
+pp_data->cap[4]->NotRange.Reserved1 = 0x0238
+pp_data->cap[4]->NotRange.StringIndex = 0
+pp_data->cap[4]->NotRange.Reserved2 = 0
+pp_data->cap[4]->NotRange.DesignatorIndex = 0
+pp_data->cap[4]->NotRange.Reserved3 = 0
+pp_data->cap[4]->NotRange.DataIndex = 19
+pp_data->cap[4]->NotRange.Reserved4 = 19
+pp_data->cap[4]->NotButton.HasNull = 0
+pp_data->cap[4]->NotButton.Reserved4 = 0x000000
+pp_data->cap[4]->NotButton.LogicalMin = -127
+pp_data->cap[4]->NotButton.LogicalMax = 127
+pp_data->cap[4]->NotButton.PhysicalMin = 0
+pp_data->cap[4]->NotButton.PhysicalMax = 0
+pp_data->cap[4]->Units = 0
+pp_data->cap[4]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0002
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 1
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 1
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[1]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[1]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[1]->Parent = 0
+pp_data->LinkCollectionArray[1]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[1]->NextSibling = 0
+pp_data->LinkCollectionArray[1]->FirstChild = 0
+pp_data->LinkCollectionArray[1]->CollectionType = 0
+pp_data->LinkCollectionArray[1]->IsAlias = 0
+pp_data->LinkCollectionArray[1]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_C52F_0002_0001_expected.rpt_desc b/src/hidapi/windows/test/data/046D_C52F_0002_0001_expected.rpt_desc
new file mode 100644
index 0000000..128c411
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C52F_0002_0001_expected.rpt_desc
@@ -0,0 +1,8 @@
+0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, 0x00,
+0x05, 0x09, 0x19, 0x01, 0x29, 0x10, 0x15, 0x00, 0x25, 0x01,
+0x75, 0x01, 0x95, 0x10, 0x81, 0x02, 0x05, 0x01, 0x09, 0x30,
+0x09, 0x31, 0x16, 0x01, 0x80, 0x26, 0xFF, 0x7F, 0x75, 0x10,
+0x95, 0x02, 0x81, 0x06, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7F,
+0x75, 0x08, 0x95, 0x01, 0x81, 0x06, 0x05, 0x0C, 0x0A, 0x38,
+0x02, 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95, 0x01, 0x81,
+0x06, 0xC0, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C52F_0002_0001_real.rpt_desc b/src/hidapi/windows/test/data/046D_C52F_0002_0001_real.rpt_desc
new file mode 100644
index 0000000..9c0521d
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C52F_0002_0001_real.rpt_desc
@@ -0,0 +1,33 @@
+Usage Page (Generic Desktop) 05 01
+Usage (Mouse) 09 02
+Collection (Application) A1 01
+ Usage (Pointer) 09 01
+ Collection (Physical) A1 00
+ Usage Page (Button) 05 09
+ Usage Minimum (Button 1) 19 01
+ Usage Maximum (Button 16) 29 10
+ Logical Minimum (0) 15 00
+ Logical Maximum (1) 25 01
+ Report Count (16) 95 10
+ Report Size (1) 75 01
+ Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
+ Usage Page (Generic Desktop) 05 01
+ Logical Minimum (-32767) 16 01 80
+ Logical Maximum (32767) 26 FF 7F
+ Report Size (16) 75 10
+ Report Count (2) 95 02
+ Usage (X) 09 30
+ Usage (Y) 09 31
+ Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit) 81 06
+ Logical Minimum (-127) 15 81
+ Logical Maximum (127) 25 7F
+ Report Size (8) 75 08
+ Report Count (1) 95 01
+ Usage (Wheel) 09 38
+ Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit) 81 06
+ Usage Page (Consumer Devices) 05 0C
+ Usage (AC Pan) 0A 38 02
+ Report Count (1) 95 01
+ Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit) 81 06
+ End Collection C0
+End Collection C0
diff --git a/src/hidapi/windows/test/data/046D_C52F_0002_FF00.pp_data b/src/hidapi/windows/test/data/046D_C52F_0002_FF00.pp_data
new file mode 100644
index 0000000..09a3689
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C52F_0002_FF00.pp_data
@@ -0,0 +1,139 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xC52F
+dev->manufacturer_string = "Logitech"
+dev->product_string = "USB Receiver"
+dev->release_number = 0x2200
+dev->interface_number = 1
+dev->usage = 0x0002
+dev->usage_page = 0xFF00
+dev->path = "\\?\hid#vid_046d&pid_c52f&mi_01&col03#8&28ca146b&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0002
+pp_data->UsagePage = 0xFF00
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 1
+pp_data->caps_info[0]->NumberOfCaps = 1
+pp_data->caps_info[0]->ReportByteLength = 20
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 1
+pp_data->caps_info[1]->LastCap = 2
+pp_data->caps_info[1]->NumberOfCaps = 1
+pp_data->caps_info[1]->ReportByteLength = 20
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 2
+pp_data->caps_info[2]->LastCap = 2
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x00D0
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0xFF00
+pp_data->cap[0]->ReportID = 0x11
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 8
+pp_data->cap[0]->ReportCount = 19
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 152
+pp_data->cap[0]->BitField = 0x00
+pp_data->cap[0]->NextBytePosition = 0x0014
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0xFF00
+pp_data->cap[0]->LinkUsage = 0x0002
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0002
+pp_data->cap[0]->NotRange.Reserved1 = 0x0002
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 255
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[1]->UsagePage = 0xFF00
+pp_data->cap[1]->ReportID = 0x11
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 8
+pp_data->cap[1]->ReportCount = 19
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 152
+pp_data->cap[1]->BitField = 0x00
+pp_data->cap[1]->NextBytePosition = 0x0014
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0xFF00
+pp_data->cap[1]->LinkUsage = 0x0002
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0002
+pp_data->cap[1]->NotRange.Reserved1 = 0x0002
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 0
+pp_data->cap[1]->NotRange.Reserved4 = 0
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 255
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0002
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0xFF00
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_C52F_0002_FF00_expected.rpt_desc b/src/hidapi/windows/test/data/046D_C52F_0002_FF00_expected.rpt_desc
new file mode 100644
index 0000000..b1654e7
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C52F_0002_FF00_expected.rpt_desc
@@ -0,0 +1,4 @@
+0x06, 0x00, 0xFF, 0x09, 0x02, 0xA1, 0x01, 0x85, 0x11, 0x09,
+0x02, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x13,
+0x81, 0x00, 0x09, 0x02, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x13, 0x91, 0x00, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C52F_0002_FF00_real.rpt_desc b/src/hidapi/windows/test/data/046D_C52F_0002_FF00_real.rpt_desc
new file mode 100644
index 0000000..68043e1
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C52F_0002_FF00_real.rpt_desc
@@ -0,0 +1,13 @@
+Usage Page (Vendor-Defined 1) 06 00 FF
+Usage (Vendor-Defined 2) 09 02
+Collection (Application) A1 01
+ Report ID (17) 85 11
+ Report Size (8) 75 08
+ Report Count (19) 95 13
+ Logical Minimum (0) 15 00
+ Logical Maximum (255) 26 FF 00
+ Usage (Vendor-Defined 2) 09 02
+ Input (Data,Ary,Abs) 81 00
+ Usage (Vendor-Defined 2) 09 02
+ Output (Data,Ary,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 00
+End Collection C0
diff --git a/src/hidapi/windows/test/data/046D_C534_0001_000C.pp_data b/src/hidapi/windows/test/data/046D_C534_0001_000C.pp_data
new file mode 100644
index 0000000..5e44a31
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0001_000C.pp_data
@@ -0,0 +1,93 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xC534
+dev->manufacturer_string = "Logitech"
+dev->product_string = "USB Receiver"
+dev->release_number = 0x2901
+dev->interface_number = 1
+dev->usage = 0x0001
+dev->usage_page = 0x000C
+dev->path = "\\?\hid#vid_046d&pid_c534&mi_01&col02#7&1ebb799e&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0001
+pp_data->UsagePage = 0x000C
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 1
+pp_data->caps_info[0]->NumberOfCaps = 1
+pp_data->caps_info[0]->ReportByteLength = 5
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 1
+pp_data->caps_info[1]->LastCap = 1
+pp_data->caps_info[1]->NumberOfCaps = 0
+pp_data->caps_info[1]->ReportByteLength = 0
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 1
+pp_data->caps_info[2]->LastCap = 1
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x0068
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x000C
+pp_data->cap[0]->ReportID = 0x03
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 16
+pp_data->cap[0]->ReportCount = 2
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 32
+pp_data->cap[0]->BitField = 0x00
+pp_data->cap[0]->NextBytePosition = 0x0005
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0x000C
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 1
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->Range.UsageMin = 0x0001
+pp_data->cap[0]->Range.UsageMax = 0x028C
+pp_data->cap[0]->Range.StringMin = 0
+pp_data->cap[0]->Range.StringMax = 0
+pp_data->cap[0]->Range.DesignatorMin = 0
+pp_data->cap[0]->Range.DesignatorMax = 0
+pp_data->cap[0]->Range.DataIndexMin = 0
+pp_data->cap[0]->Range.DataIndexMax = 651
+pp_data->cap[0]->Button.LogicalMin = 1
+pp_data->cap[0]->Button.LogicalMax = 652
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x000C
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_C534_0001_000C_expected.rpt_desc b/src/hidapi/windows/test/data/046D_C534_0001_000C_expected.rpt_desc
new file mode 100644
index 0000000..85953ae
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0001_000C_expected.rpt_desc
@@ -0,0 +1,3 @@
+0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x03, 0x19, 0x01,
+0x2A, 0x8C, 0x02, 0x15, 0x01, 0x26, 0x8C, 0x02, 0x75, 0x10,
+0x95, 0x02, 0x81, 0x00, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C534_0001_000C_real.rpt_desc b/src/hidapi/windows/test/data/046D_C534_0001_000C_real.rpt_desc
new file mode 100644
index 0000000..e08f1f3
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0001_000C_real.rpt_desc
@@ -0,0 +1,18 @@
+macOS USB prober output for Logitech USB Receiver
+ 05 0C 09 01 A1 01
+ 85 03 75 10 95 02 15 01 26 8C 02 19 01 2A 8C 02 81 00
+ C0
+
+ Parser output:
+0x05, 0x0C, // Usage Page (Consumer)
+0x09, 0x01, // Usage (Consumer Control)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x03, // Report ID (3)
+0x75, 0x10, // Report Size (16)
+0x95, 0x02, // Report Count (2)
+0x15, 0x01, // Logical Minimum (1)
+0x26, 0x8C, 0x02, // Logical Maximum (652)
+0x19, 0x01, // Usage Minimum (Consumer Control)
+0x2A, 0x8C, 0x02, // Usage Maximum (AC Send)
+0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C534_0001_FF00.pp_data b/src/hidapi/windows/test/data/046D_C534_0001_FF00.pp_data
new file mode 100644
index 0000000..6d42a15
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0001_FF00.pp_data
@@ -0,0 +1,139 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xC534
+dev->manufacturer_string = "Logitech"
+dev->product_string = "USB Receiver"
+dev->release_number = 0x2901
+dev->interface_number = 1
+dev->usage = 0x0001
+dev->usage_page = 0xFF00
+dev->path = "\\?\hid#vid_046d&pid_c534&mi_01&col04#7&1ebb799e&0&0003#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0001
+pp_data->UsagePage = 0xFF00
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 1
+pp_data->caps_info[0]->NumberOfCaps = 1
+pp_data->caps_info[0]->ReportByteLength = 7
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 1
+pp_data->caps_info[1]->LastCap = 2
+pp_data->caps_info[1]->NumberOfCaps = 1
+pp_data->caps_info[1]->ReportByteLength = 7
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 2
+pp_data->caps_info[2]->LastCap = 2
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x00D0
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0xFF00
+pp_data->cap[0]->ReportID = 0x10
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 8
+pp_data->cap[0]->ReportCount = 6
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 48
+pp_data->cap[0]->BitField = 0x00
+pp_data->cap[0]->NextBytePosition = 0x0007
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0xFF00
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0001
+pp_data->cap[0]->NotRange.Reserved1 = 0x0001
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 255
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[1]->UsagePage = 0xFF00
+pp_data->cap[1]->ReportID = 0x10
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 8
+pp_data->cap[1]->ReportCount = 6
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 48
+pp_data->cap[1]->BitField = 0x00
+pp_data->cap[1]->NextBytePosition = 0x0007
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0xFF00
+pp_data->cap[1]->LinkUsage = 0x0001
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0001
+pp_data->cap[1]->NotRange.Reserved1 = 0x0001
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 0
+pp_data->cap[1]->NotRange.Reserved4 = 0
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 255
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0xFF00
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_C534_0001_FF00_expected.rpt_desc b/src/hidapi/windows/test/data/046D_C534_0001_FF00_expected.rpt_desc
new file mode 100644
index 0000000..812bd2a
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0001_FF00_expected.rpt_desc
@@ -0,0 +1,4 @@
+0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x10, 0x09,
+0x01, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x06,
+0x81, 0x00, 0x09, 0x01, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x06, 0x91, 0x00, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C534_0001_FF00_real.rpt_desc b/src/hidapi/windows/test/data/046D_C534_0001_FF00_real.rpt_desc
new file mode 100644
index 0000000..953193c
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0001_FF00_real.rpt_desc
@@ -0,0 +1,20 @@
+macOS USB prober output for Logitech USB Receiver
+
+06 00 FF 09 01 A1 01 85 10 75 08 95 06
+15 00 26 FF 00 09 01 81
+00 09 01 91 00 C0
+
+Parser Output:
+0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
+0x09, 0x01, // Usage (0x01)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x10, // Report ID (16)
+0x75, 0x08, // Report Size (8)
+0x95, 0x06, // Report Count (6)
+0x15, 0x00, // Logical Minimum (0)
+0x26, 0xFF, 0x00, // Logical Maximum (255)
+0x09, 0x01, // Usage (0x01)
+0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x09, 0x01, // Usage (0x01)
+0x91, 0x00, // Output (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0xC0, // End Collection
diff --git a/src/hidapi/windows/test/data/046D_C534_0002_0001.pp_data b/src/hidapi/windows/test/data/046D_C534_0002_0001.pp_data
new file mode 100644
index 0000000..f50d8a2
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0002_0001.pp_data
@@ -0,0 +1,302 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xC534
+dev->manufacturer_string = "Logitech"
+dev->product_string = "USB Receiver"
+dev->release_number = 0x2901
+dev->interface_number = 1
+dev->usage = 0x0002
+dev->usage_page = 0x0001
+dev->path = "\\?\hid#vid_046d&pid_c534&mi_01&col01#7&1ebb799e&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0002
+pp_data->UsagePage = 0x0001
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 5
+pp_data->caps_info[0]->NumberOfCaps = 5
+pp_data->caps_info[0]->ReportByteLength = 8
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 5
+pp_data->caps_info[1]->LastCap = 5
+pp_data->caps_info[1]->NumberOfCaps = 0
+pp_data->caps_info[1]->ReportByteLength = 0
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 5
+pp_data->caps_info[2]->LastCap = 5
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x0208
+pp_data->NumberLinkCollectionNodes = 2
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x0009
+pp_data->cap[0]->ReportID = 0x02
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 1
+pp_data->cap[0]->ReportCount = 16
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 16
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0003
+pp_data->cap[0]->LinkCollection = 0x0001
+pp_data->cap[0]->LinkUsagePage = 0x0001
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 1
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->Range.UsageMin = 0x0001
+pp_data->cap[0]->Range.UsageMax = 0x0010
+pp_data->cap[0]->Range.StringMin = 0
+pp_data->cap[0]->Range.StringMax = 0
+pp_data->cap[0]->Range.DesignatorMin = 0
+pp_data->cap[0]->Range.DesignatorMax = 0
+pp_data->cap[0]->Range.DataIndexMin = 0
+pp_data->cap[0]->Range.DataIndexMax = 15
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x0001
+pp_data->cap[1]->ReportID = 0x02
+pp_data->cap[1]->BitPosition = 4
+pp_data->cap[1]->BitSize = 12
+pp_data->cap[1]->ReportCount = 1
+pp_data->cap[1]->BytePosition = 0x0004
+pp_data->cap[1]->BitCount = 12
+pp_data->cap[1]->BitField = 0x06
+pp_data->cap[1]->NextBytePosition = 0x0006
+pp_data->cap[1]->LinkCollection = 0x0001
+pp_data->cap[1]->LinkUsagePage = 0x0001
+pp_data->cap[1]->LinkUsage = 0x0001
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 0
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 0
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0031
+pp_data->cap[1]->NotRange.Reserved1 = 0x0031
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 16
+pp_data->cap[1]->NotRange.Reserved4 = 16
+pp_data->cap[1]->NotButton.HasNull = 0
+pp_data->cap[1]->NotButton.Reserved4 = 0x000000
+pp_data->cap[1]->NotButton.LogicalMin = -2047
+pp_data->cap[1]->NotButton.LogicalMax = 2047
+pp_data->cap[1]->NotButton.PhysicalMin = 0
+pp_data->cap[1]->NotButton.PhysicalMax = 0
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+pp_data->cap[2]->UsagePage = 0x0001
+pp_data->cap[2]->ReportID = 0x02
+pp_data->cap[2]->BitPosition = 0
+pp_data->cap[2]->BitSize = 12
+pp_data->cap[2]->ReportCount = 1
+pp_data->cap[2]->BytePosition = 0x0003
+pp_data->cap[2]->BitCount = 12
+pp_data->cap[2]->BitField = 0x06
+pp_data->cap[2]->NextBytePosition = 0x0005
+pp_data->cap[2]->LinkCollection = 0x0001
+pp_data->cap[2]->LinkUsagePage = 0x0001
+pp_data->cap[2]->LinkUsage = 0x0001
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 0
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 0
+pp_data->cap[2]->IsRange = 0
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->NotRange.Usage = 0x0030
+pp_data->cap[2]->NotRange.Reserved1 = 0x0030
+pp_data->cap[2]->NotRange.StringIndex = 0
+pp_data->cap[2]->NotRange.Reserved2 = 0
+pp_data->cap[2]->NotRange.DesignatorIndex = 0
+pp_data->cap[2]->NotRange.Reserved3 = 0
+pp_data->cap[2]->NotRange.DataIndex = 17
+pp_data->cap[2]->NotRange.Reserved4 = 17
+pp_data->cap[2]->NotButton.HasNull = 0
+pp_data->cap[2]->NotButton.Reserved4 = 0x000000
+pp_data->cap[2]->NotButton.LogicalMin = -2047
+pp_data->cap[2]->NotButton.LogicalMax = 2047
+pp_data->cap[2]->NotButton.PhysicalMin = 0
+pp_data->cap[2]->NotButton.PhysicalMax = 0
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+pp_data->cap[3]->UsagePage = 0x0001
+pp_data->cap[3]->ReportID = 0x02
+pp_data->cap[3]->BitPosition = 0
+pp_data->cap[3]->BitSize = 8
+pp_data->cap[3]->ReportCount = 1
+pp_data->cap[3]->BytePosition = 0x0006
+pp_data->cap[3]->BitCount = 8
+pp_data->cap[3]->BitField = 0x06
+pp_data->cap[3]->NextBytePosition = 0x0007
+pp_data->cap[3]->LinkCollection = 0x0001
+pp_data->cap[3]->LinkUsagePage = 0x0001
+pp_data->cap[3]->LinkUsage = 0x0001
+pp_data->cap[3]->IsMultipleItemsForArray = 0
+pp_data->cap[3]->IsButtonCap = 0
+pp_data->cap[3]->IsPadding = 0
+pp_data->cap[3]->IsAbsolute = 0
+pp_data->cap[3]->IsRange = 0
+pp_data->cap[3]->IsAlias = 0
+pp_data->cap[3]->IsStringRange = 0
+pp_data->cap[3]->IsDesignatorRange = 0
+pp_data->cap[3]->Reserved1 = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[3]->NotRange.Usage = 0x0038
+pp_data->cap[3]->NotRange.Reserved1 = 0x0038
+pp_data->cap[3]->NotRange.StringIndex = 0
+pp_data->cap[3]->NotRange.Reserved2 = 0
+pp_data->cap[3]->NotRange.DesignatorIndex = 0
+pp_data->cap[3]->NotRange.Reserved3 = 0
+pp_data->cap[3]->NotRange.DataIndex = 18
+pp_data->cap[3]->NotRange.Reserved4 = 18
+pp_data->cap[3]->NotButton.HasNull = 0
+pp_data->cap[3]->NotButton.Reserved4 = 0x000000
+pp_data->cap[3]->NotButton.LogicalMin = -127
+pp_data->cap[3]->NotButton.LogicalMax = 127
+pp_data->cap[3]->NotButton.PhysicalMin = 0
+pp_data->cap[3]->NotButton.PhysicalMax = 0
+pp_data->cap[3]->Units = 0
+pp_data->cap[3]->UnitsExp = 0
+
+pp_data->cap[4]->UsagePage = 0x000C
+pp_data->cap[4]->ReportID = 0x02
+pp_data->cap[4]->BitPosition = 0
+pp_data->cap[4]->BitSize = 8
+pp_data->cap[4]->ReportCount = 1
+pp_data->cap[4]->BytePosition = 0x0007
+pp_data->cap[4]->BitCount = 8
+pp_data->cap[4]->BitField = 0x06
+pp_data->cap[4]->NextBytePosition = 0x0008
+pp_data->cap[4]->LinkCollection = 0x0001
+pp_data->cap[4]->LinkUsagePage = 0x0001
+pp_data->cap[4]->LinkUsage = 0x0001
+pp_data->cap[4]->IsMultipleItemsForArray = 0
+pp_data->cap[4]->IsButtonCap = 0
+pp_data->cap[4]->IsPadding = 0
+pp_data->cap[4]->IsAbsolute = 0
+pp_data->cap[4]->IsRange = 0
+pp_data->cap[4]->IsAlias = 0
+pp_data->cap[4]->IsStringRange = 0
+pp_data->cap[4]->IsDesignatorRange = 0
+pp_data->cap[4]->Reserved1 = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[4]->NotRange.Usage = 0x0238
+pp_data->cap[4]->NotRange.Reserved1 = 0x0238
+pp_data->cap[4]->NotRange.StringIndex = 0
+pp_data->cap[4]->NotRange.Reserved2 = 0
+pp_data->cap[4]->NotRange.DesignatorIndex = 0
+pp_data->cap[4]->NotRange.Reserved3 = 0
+pp_data->cap[4]->NotRange.DataIndex = 19
+pp_data->cap[4]->NotRange.Reserved4 = 19
+pp_data->cap[4]->NotButton.HasNull = 0
+pp_data->cap[4]->NotButton.Reserved4 = 0x000000
+pp_data->cap[4]->NotButton.LogicalMin = -127
+pp_data->cap[4]->NotButton.LogicalMax = 127
+pp_data->cap[4]->NotButton.PhysicalMin = 0
+pp_data->cap[4]->NotButton.PhysicalMax = 0
+pp_data->cap[4]->Units = 0
+pp_data->cap[4]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0002
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 1
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 1
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[1]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[1]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[1]->Parent = 0
+pp_data->LinkCollectionArray[1]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[1]->NextSibling = 0
+pp_data->LinkCollectionArray[1]->FirstChild = 0
+pp_data->LinkCollectionArray[1]->CollectionType = 0
+pp_data->LinkCollectionArray[1]->IsAlias = 0
+pp_data->LinkCollectionArray[1]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_C534_0002_0001_expected.rpt_desc b/src/hidapi/windows/test/data/046D_C534_0002_0001_expected.rpt_desc
new file mode 100644
index 0000000..48701c6
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0002_0001_expected.rpt_desc
@@ -0,0 +1,8 @@
+0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, 0x00,
+0x85, 0x02, 0x05, 0x09, 0x19, 0x01, 0x29, 0x10, 0x15, 0x00,
+0x25, 0x01, 0x75, 0x01, 0x95, 0x10, 0x81, 0x02, 0x05, 0x01,
+0x09, 0x30, 0x09, 0x31, 0x16, 0x01, 0xF8, 0x26, 0xFF, 0x07,
+0x75, 0x0C, 0x95, 0x02, 0x81, 0x06, 0x09, 0x38, 0x15, 0x81,
+0x25, 0x7F, 0x75, 0x08, 0x95, 0x01, 0x81, 0x06, 0x05, 0x0C,
+0x0A, 0x38, 0x02, 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95,
+0x01, 0x81, 0x06, 0xC0, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C534_0002_0001_real.rpt_desc b/src/hidapi/windows/test/data/046D_C534_0002_0001_real.rpt_desc
new file mode 100644
index 0000000..1c5dea9
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0002_0001_real.rpt_desc
@@ -0,0 +1,44 @@
+
+05 01 09 02 A1 01 85 02 09 01 A1 00 05 09 19 01
+29 10 15 00 25 01 95 10 75 01 81 02 05 01 16 01
+F8 26 FF 07 75 0C 95 02 09 30 09 31 81 06 15 81
+25 7F 75 08 95 01 09 38 81 06 05 0C 0A 38 02 95
+01 81 06 C0 C0
+
+Parser Output:
+0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+0x09, 0x02, // Usage (Mouse)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x02, // Report ID (2)
+0x09, 0x01, // Usage (Pointer)
+0xA1, 0x00, // Collection (Physical)
+0x05, 0x09, // Usage Page (Button)
+0x19, 0x01, // Usage Minimum (0x01)
+0x29, 0x10, // Usage Maximum (0x10)
+0x15, 0x00, // Logical Minimum (0)
+0x25, 0x01, // Logical Maximum (1)
+0x95, 0x10, // Report Count (16)
+0x75, 0x01, // Report Size (1)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+0x16, 0x01, 0xF8, // Logical Minimum (-2047)
+0x26, 0xFF, 0x07, // Logical Maximum (2047)
+0x75, 0x0C, // Report Size (12)
+0x95, 0x02, // Report Count (2)
+0x09, 0x30, // Usage (X)
+0x09, 0x31, // Usage (Y)
+0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+0x15, 0x81, // Logical Minimum (-127)
+0x25, 0x7F, // Logical Maximum (127)
+0x75, 0x08, // Report Size (8)
+0x95, 0x01, // Report Count (1)
+0x09, 0x38, // Usage (Wheel)
+0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+0x05, 0x0C, // Usage Page (Consumer)
+0x0A, 0x38, 0x02, // Usage (AC Pan)
+0x95, 0x01, // Report Count (1)
+0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
+0xC0, // End Collection
+
+// 69 bytes
diff --git a/src/hidapi/windows/test/data/046D_C534_0002_FF00.pp_data b/src/hidapi/windows/test/data/046D_C534_0002_FF00.pp_data
new file mode 100644
index 0000000..b5fae3a
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0002_FF00.pp_data
@@ -0,0 +1,139 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xC534
+dev->manufacturer_string = "Logitech"
+dev->product_string = "USB Receiver"
+dev->release_number = 0x2901
+dev->interface_number = 1
+dev->usage = 0x0002
+dev->usage_page = 0xFF00
+dev->path = "\\?\hid#vid_046d&pid_c534&mi_01&col05#7&1ebb799e&0&0004#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0002
+pp_data->UsagePage = 0xFF00
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 1
+pp_data->caps_info[0]->NumberOfCaps = 1
+pp_data->caps_info[0]->ReportByteLength = 20
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 1
+pp_data->caps_info[1]->LastCap = 2
+pp_data->caps_info[1]->NumberOfCaps = 1
+pp_data->caps_info[1]->ReportByteLength = 20
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 2
+pp_data->caps_info[2]->LastCap = 2
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x00D0
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0xFF00
+pp_data->cap[0]->ReportID = 0x11
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 8
+pp_data->cap[0]->ReportCount = 19
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 152
+pp_data->cap[0]->BitField = 0x00
+pp_data->cap[0]->NextBytePosition = 0x0014
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0xFF00
+pp_data->cap[0]->LinkUsage = 0x0002
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0002
+pp_data->cap[0]->NotRange.Reserved1 = 0x0002
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 255
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[1]->UsagePage = 0xFF00
+pp_data->cap[1]->ReportID = 0x11
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 8
+pp_data->cap[1]->ReportCount = 19
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 152
+pp_data->cap[1]->BitField = 0x00
+pp_data->cap[1]->NextBytePosition = 0x0014
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0xFF00
+pp_data->cap[1]->LinkUsage = 0x0002
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0002
+pp_data->cap[1]->NotRange.Reserved1 = 0x0002
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 0
+pp_data->cap[1]->NotRange.Reserved4 = 0
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 255
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0002
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0xFF00
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_C534_0002_FF00_expected.rpt_desc b/src/hidapi/windows/test/data/046D_C534_0002_FF00_expected.rpt_desc
new file mode 100644
index 0000000..b1654e7
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0002_FF00_expected.rpt_desc
@@ -0,0 +1,4 @@
+0x06, 0x00, 0xFF, 0x09, 0x02, 0xA1, 0x01, 0x85, 0x11, 0x09,
+0x02, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x13,
+0x81, 0x00, 0x09, 0x02, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x13, 0x91, 0x00, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C534_0002_FF00_real.rpt_desc b/src/hidapi/windows/test/data/046D_C534_0002_FF00_real.rpt_desc
new file mode 100644
index 0000000..42a0ad8
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0002_FF00_real.rpt_desc
@@ -0,0 +1,22 @@
+macOS USB prober output for Logitech USB Receiver
+
+06 00 FF 09 02 A1 01 85 11
+75 08 95 13 15 00 26 FF
+00 09 02 81 00 09 02 91 00 C0
+
+Parser output:
+0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
+0x09, 0x02, // Usage (0x02)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x11, // Report ID (17)
+0x75, 0x08, // Report Size (8)
+0x95, 0x13, // Report Count (19)
+0x15, 0x00, // Logical Minimum (0)
+0x26, 0xFF, 0x00, // Logical Maximum (255)
+0x09, 0x02, // Usage (0x02)
+0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x09, 0x02, // Usage (0x02)
+0x91, 0x00, // Output (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0xC0, // End Collection
+
+// 27 bytes
diff --git a/src/hidapi/windows/test/data/046D_C534_0006_0001.pp_data b/src/hidapi/windows/test/data/046D_C534_0006_0001.pp_data
new file mode 100644
index 0000000..2b42d5f
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0006_0001.pp_data
@@ -0,0 +1,185 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xC534
+dev->manufacturer_string = "Logitech"
+dev->product_string = "USB Receiver"
+dev->release_number = 0x2901
+dev->interface_number = 0
+dev->usage = 0x0006
+dev->usage_page = 0x0001
+dev->path = "\\?\hid#vid_046d&pid_c534&mi_00#7&51bc424&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0006
+pp_data->UsagePage = 0x0001
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 2
+pp_data->caps_info[0]->NumberOfCaps = 2
+pp_data->caps_info[0]->ReportByteLength = 9
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 2
+pp_data->caps_info[1]->LastCap = 3
+pp_data->caps_info[1]->NumberOfCaps = 1
+pp_data->caps_info[1]->ReportByteLength = 2
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 3
+pp_data->caps_info[2]->LastCap = 3
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x0138
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x0007
+pp_data->cap[0]->ReportID = 0x00
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 1
+pp_data->cap[0]->ReportCount = 8
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 8
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0002
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0x0001
+pp_data->cap[0]->LinkUsage = 0x0006
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 1
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->Range.UsageMin = 0x00E0
+pp_data->cap[0]->Range.UsageMax = 0x00E7
+pp_data->cap[0]->Range.StringMin = 0
+pp_data->cap[0]->Range.StringMax = 0
+pp_data->cap[0]->Range.DesignatorMin = 0
+pp_data->cap[0]->Range.DesignatorMax = 0
+pp_data->cap[0]->Range.DataIndexMin = 0
+pp_data->cap[0]->Range.DataIndexMax = 7
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x0007
+pp_data->cap[1]->ReportID = 0x00
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 8
+pp_data->cap[1]->ReportCount = 6
+pp_data->cap[1]->BytePosition = 0x0003
+pp_data->cap[1]->BitCount = 48
+pp_data->cap[1]->BitField = 0x00
+pp_data->cap[1]->NextBytePosition = 0x0009
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0x0001
+pp_data->cap[1]->LinkUsage = 0x0006
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 1
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->Range.UsageMin = 0x0000
+pp_data->cap[1]->Range.UsageMax = 0x00A4
+pp_data->cap[1]->Range.StringMin = 0
+pp_data->cap[1]->Range.StringMax = 0
+pp_data->cap[1]->Range.DesignatorMin = 0
+pp_data->cap[1]->Range.DesignatorMax = 0
+pp_data->cap[1]->Range.DataIndexMin = 8
+pp_data->cap[1]->Range.DataIndexMax = 172
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 164
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[2]->UsagePage = 0x0008
+pp_data->cap[2]->ReportID = 0x00
+pp_data->cap[2]->BitPosition = 0
+pp_data->cap[2]->BitSize = 1
+pp_data->cap[2]->ReportCount = 5
+pp_data->cap[2]->BytePosition = 0x0001
+pp_data->cap[2]->BitCount = 5
+pp_data->cap[2]->BitField = 0x02
+pp_data->cap[2]->NextBytePosition = 0x0002
+pp_data->cap[2]->LinkCollection = 0x0000
+pp_data->cap[2]->LinkUsagePage = 0x0001
+pp_data->cap[2]->LinkUsage = 0x0006
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 1
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 1
+pp_data->cap[2]->IsRange = 1
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->Range.UsageMin = 0x0001
+pp_data->cap[2]->Range.UsageMax = 0x0005
+pp_data->cap[2]->Range.StringMin = 0
+pp_data->cap[2]->Range.StringMax = 0
+pp_data->cap[2]->Range.DesignatorMin = 0
+pp_data->cap[2]->Range.DesignatorMax = 0
+pp_data->cap[2]->Range.DataIndexMin = 0
+pp_data->cap[2]->Range.DataIndexMax = 4
+pp_data->cap[2]->Button.LogicalMin = 0
+pp_data->cap[2]->Button.LogicalMax = 0
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0006
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_C534_0006_0001_expected.rpt_desc b/src/hidapi/windows/test/data/046D_C534_0006_0001_expected.rpt_desc
new file mode 100644
index 0000000..d41d471
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0006_0001_expected.rpt_desc
@@ -0,0 +1,7 @@
+0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0,
+0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08,
+0x81, 0x02, 0x75, 0x08, 0x95, 0x01, 0x81, 0x03, 0x19, 0x00,
+0x29, 0xA4, 0x15, 0x00, 0x26, 0xA4, 0x00, 0x75, 0x08, 0x95,
+0x06, 0x81, 0x00, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x15,
+0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x05, 0x91, 0x02, 0x75,
+0x03, 0x95, 0x01, 0x91, 0x03, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C534_0006_0001_real.rpt_desc b/src/hidapi/windows/test/data/046D_C534_0006_0001_real.rpt_desc
new file mode 100644
index 0000000..d65aa57
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0006_0001_real.rpt_desc
@@ -0,0 +1,42 @@
+macOS USB prober output for Logitech USB Receiver
+
+
+Type: 0x22 (Report Descriptor)
+Length (and contents): 59
+ Raw Descriptor (hex) 0000: 05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01
+ Raw Descriptor (hex) 0010: 75 01 95 08 81 02 81 03 95 05 05 08 19 01 29 05
+ Raw Descriptor (hex) 0020: 91 02 95 01 75 03 91 01 95 06 75 08 15 00 26 A4
+ Raw Descriptor (hex) 0030: 00 05 07 19 00 2A A4 00 81 00 C0
+
+Parser output:
+0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+0x09, 0x06, // Usage (Keyboard)
+0xA1, 0x01, // Collection (Application)
+0x05, 0x07, // Usage Page (Kbrd/Keypad)
+0x19, 0xE0, // Usage Minimum (0xE0)
+0x29, 0xE7, // Usage Maximum (0xE7)
+0x15, 0x00, // Logical Minimum (0)
+0x25, 0x01, // Logical Maximum (1)
+0x75, 0x01, // Report Size (1)
+0x95, 0x08, // Report Count (8)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x95, 0x05, // Report Count (5)
+0x05, 0x08, // Usage Page (LEDs)
+0x19, 0x01, // Usage Minimum (Num Lock)
+0x29, 0x05, // Usage Maximum (Kana)
+0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x95, 0x01, // Report Count (1)
+0x75, 0x03, // Report Size (3)
+0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x95, 0x06, // Report Count (6)
+0x75, 0x08, // Report Size (8)
+0x15, 0x00, // Logical Minimum (0)
+0x26, 0xA4, 0x00, // Logical Maximum (164)
+0x05, 0x07, // Usage Page (Kbrd/Keypad)
+0x19, 0x00, // Usage Minimum (0x00)
+0x2A, 0xA4, 0x00, // Usage Maximum (0xA4)
+0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
+
+// 59 bytes
diff --git a/src/hidapi/windows/test/data/046D_C534_0080_0001.pp_data b/src/hidapi/windows/test/data/046D_C534_0080_0001.pp_data
new file mode 100644
index 0000000..a829f70
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0080_0001.pp_data
@@ -0,0 +1,185 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x046D
+dev->product_id = 0xC534
+dev->manufacturer_string = "Logitech"
+dev->product_string = "USB Receiver"
+dev->release_number = 0x2901
+dev->interface_number = 1
+dev->usage = 0x0080
+dev->usage_page = 0x0001
+dev->path = "\\?\hid#vid_046d&pid_c534&mi_01&col03#7&1ebb799e&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0080
+pp_data->UsagePage = 0x0001
+pp_data->Reserved = 0x00038000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 3
+pp_data->caps_info[0]->NumberOfCaps = 3
+pp_data->caps_info[0]->ReportByteLength = 2
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 3
+pp_data->caps_info[1]->LastCap = 3
+pp_data->caps_info[1]->NumberOfCaps = 0
+pp_data->caps_info[1]->ReportByteLength = 0
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 3
+pp_data->caps_info[2]->LastCap = 3
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x0138
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x0001
+pp_data->cap[0]->ReportID = 0x04
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 2
+pp_data->cap[0]->ReportCount = 1
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 2
+pp_data->cap[0]->BitField = 0x60
+pp_data->cap[0]->NextBytePosition = 0x0002
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0x0001
+pp_data->cap[0]->LinkUsage = 0x0080
+pp_data->cap[0]->IsMultipleItemsForArray = 1
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0083
+pp_data->cap[0]->NotRange.Reserved1 = 0x0083
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 2
+pp_data->cap[0]->NotRange.Reserved4 = 2
+pp_data->cap[0]->Button.LogicalMin = 1
+pp_data->cap[0]->Button.LogicalMax = 3
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x0001
+pp_data->cap[1]->ReportID = 0x04
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 2
+pp_data->cap[1]->ReportCount = 1
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 2
+pp_data->cap[1]->BitField = 0x60
+pp_data->cap[1]->NextBytePosition = 0x0002
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0x0001
+pp_data->cap[1]->LinkUsage = 0x0080
+pp_data->cap[1]->IsMultipleItemsForArray = 1
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0081
+pp_data->cap[1]->NotRange.Reserved1 = 0x0081
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 1
+pp_data->cap[1]->NotRange.Reserved4 = 1
+pp_data->cap[1]->Button.LogicalMin = 1
+pp_data->cap[1]->Button.LogicalMax = 3
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+pp_data->cap[2]->UsagePage = 0x0001
+pp_data->cap[2]->ReportID = 0x04
+pp_data->cap[2]->BitPosition = 0
+pp_data->cap[2]->BitSize = 2
+pp_data->cap[2]->ReportCount = 1
+pp_data->cap[2]->BytePosition = 0x0001
+pp_data->cap[2]->BitCount = 2
+pp_data->cap[2]->BitField = 0x60
+pp_data->cap[2]->NextBytePosition = 0x0002
+pp_data->cap[2]->LinkCollection = 0x0000
+pp_data->cap[2]->LinkUsagePage = 0x0001
+pp_data->cap[2]->LinkUsage = 0x0080
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 1
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 1
+pp_data->cap[2]->IsRange = 0
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->NotRange.Usage = 0x0082
+pp_data->cap[2]->NotRange.Reserved1 = 0x0082
+pp_data->cap[2]->NotRange.StringIndex = 0
+pp_data->cap[2]->NotRange.Reserved2 = 0
+pp_data->cap[2]->NotRange.DesignatorIndex = 0
+pp_data->cap[2]->NotRange.Reserved3 = 0
+pp_data->cap[2]->NotRange.DataIndex = 0
+pp_data->cap[2]->NotRange.Reserved4 = 0
+pp_data->cap[2]->Button.LogicalMin = 1
+pp_data->cap[2]->Button.LogicalMax = 3
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0080
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x0001
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/046D_C534_0080_0001_expected.rpt_desc b/src/hidapi/windows/test/data/046D_C534_0080_0001_expected.rpt_desc
new file mode 100644
index 0000000..e7a9677
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0080_0001_expected.rpt_desc
@@ -0,0 +1,4 @@
+0x05, 0x01, 0x09, 0x80, 0xA1, 0x01, 0x85, 0x04, 0x09, 0x82,
+0x09, 0x81, 0x09, 0x83, 0x15, 0x01, 0x25, 0x03, 0x75, 0x02,
+0x95, 0x01, 0x81, 0x60, 0x75, 0x06, 0x95, 0x01, 0x81, 0x03,
+0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/046D_C534_0080_0001_real.rpt_desc b/src/hidapi/windows/test/data/046D_C534_0080_0001_real.rpt_desc
new file mode 100644
index 0000000..7ebe8c9
--- /dev/null
+++ b/src/hidapi/windows/test/data/046D_C534_0080_0001_real.rpt_desc
@@ -0,0 +1,22 @@
+
+05 01 09 80 A1 01 85 04 75 02 95 01 15 01 25 03
+09 82 09 81 09 83 81 60 75 06 81 03 C0
+
+Parser output:
+0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+0x09, 0x80, // Usage (Sys Control)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x04, // Report ID (4)
+0x75, 0x02, // Report Size (2)
+0x95, 0x01, // Report Count (1)
+0x15, 0x01, // Logical Minimum (1)
+0x25, 0x03, // Logical Maximum (3)
+0x09, 0x82, // Usage (Sys Sleep)
+0x09, 0x81, // Usage (Sys Power Down)
+0x09, 0x83, // Usage (Sys Wake Up)
+0x81, 0x60, // Input (Data,Array,Abs,No Wrap,Linear,No Preferred State,Null State)
+0x75, 0x06, // Report Size (6)
+0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
+
+// 29 bytes
diff --git a/src/hidapi/windows/test/data/047F_C056_0001_000C.pp_data b/src/hidapi/windows/test/data/047F_C056_0001_000C.pp_data
new file mode 100644
index 0000000..87da2b2
--- /dev/null
+++ b/src/hidapi/windows/test/data/047F_C056_0001_000C.pp_data
@@ -0,0 +1,385 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x047F
+dev->product_id = 0xC056
+dev->manufacturer_string = "Plantronics"
+dev->product_string = "Plantronics Blackwire 3220 Series"
+dev->release_number = 0x0210
+dev->interface_number = 3
+dev->usage = 0x0001
+dev->usage_page = 0x000C
+dev->path = "\\?\hid#vid_047f&pid_c056&mi_03&col01#f&39e6f119&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0001
+pp_data->UsagePage = 0x000C
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 5
+pp_data->caps_info[0]->NumberOfCaps = 5
+pp_data->caps_info[0]->ReportByteLength = 33
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 5
+pp_data->caps_info[1]->LastCap = 7
+pp_data->caps_info[1]->NumberOfCaps = 2
+pp_data->caps_info[1]->ReportByteLength = 37
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 7
+pp_data->caps_info[2]->LastCap = 7
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x02D8
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x000C
+pp_data->cap[0]->ReportID = 0x01
+pp_data->cap[0]->BitPosition = 1
+pp_data->cap[0]->BitSize = 1
+pp_data->cap[0]->ReportCount = 1
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 1
+pp_data->cap[0]->BitField = 0x06
+pp_data->cap[0]->NextBytePosition = 0x0002
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0x000C
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 0
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x00EA
+pp_data->cap[0]->NotRange.Reserved1 = 0x00EA
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x000C
+pp_data->cap[1]->ReportID = 0x01
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 1
+pp_data->cap[1]->ReportCount = 1
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 1
+pp_data->cap[1]->BitField = 0x06
+pp_data->cap[1]->NextBytePosition = 0x0002
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0x000C
+pp_data->cap[1]->LinkUsage = 0x0001
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 0
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x00E9
+pp_data->cap[1]->NotRange.Reserved1 = 0x00E9
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 1
+pp_data->cap[1]->NotRange.Reserved4 = 1
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 0
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+pp_data->cap[2]->UsagePage = 0x000C
+pp_data->cap[2]->ReportID = 0x02
+pp_data->cap[2]->BitPosition = 0
+pp_data->cap[2]->BitSize = 1
+pp_data->cap[2]->ReportCount = 16
+pp_data->cap[2]->BytePosition = 0x0001
+pp_data->cap[2]->BitCount = 16
+pp_data->cap[2]->BitField = 0x02
+pp_data->cap[2]->NextBytePosition = 0x0003
+pp_data->cap[2]->LinkCollection = 0x0000
+pp_data->cap[2]->LinkUsagePage = 0x000C
+pp_data->cap[2]->LinkUsage = 0x0001
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 1
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 1
+pp_data->cap[2]->IsRange = 0
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->NotRange.Usage = 0x0000
+pp_data->cap[2]->NotRange.Reserved1 = 0x0000
+pp_data->cap[2]->NotRange.StringIndex = 0
+pp_data->cap[2]->NotRange.Reserved2 = 0
+pp_data->cap[2]->NotRange.DesignatorIndex = 0
+pp_data->cap[2]->NotRange.Reserved3 = 0
+pp_data->cap[2]->NotRange.DataIndex = 2
+pp_data->cap[2]->NotRange.Reserved4 = 2
+pp_data->cap[2]->Button.LogicalMin = 0
+pp_data->cap[2]->Button.LogicalMax = 0
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+pp_data->cap[3]->UsagePage = 0x000C
+pp_data->cap[3]->ReportID = 0x05
+pp_data->cap[3]->BitPosition = 0
+pp_data->cap[3]->BitSize = 8
+pp_data->cap[3]->ReportCount = 32
+pp_data->cap[3]->BytePosition = 0x0001
+pp_data->cap[3]->BitCount = 256
+pp_data->cap[3]->BitField = 0x02
+pp_data->cap[3]->NextBytePosition = 0x0021
+pp_data->cap[3]->LinkCollection = 0x0000
+pp_data->cap[3]->LinkUsagePage = 0x000C
+pp_data->cap[3]->LinkUsage = 0x0001
+pp_data->cap[3]->IsMultipleItemsForArray = 0
+pp_data->cap[3]->IsButtonCap = 0
+pp_data->cap[3]->IsPadding = 0
+pp_data->cap[3]->IsAbsolute = 1
+pp_data->cap[3]->IsRange = 0
+pp_data->cap[3]->IsAlias = 0
+pp_data->cap[3]->IsStringRange = 0
+pp_data->cap[3]->IsDesignatorRange = 0
+pp_data->cap[3]->Reserved1 = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[3]->NotRange.Usage = 0x0000
+pp_data->cap[3]->NotRange.Reserved1 = 0x0000
+pp_data->cap[3]->NotRange.StringIndex = 0
+pp_data->cap[3]->NotRange.Reserved2 = 0
+pp_data->cap[3]->NotRange.DesignatorIndex = 0
+pp_data->cap[3]->NotRange.Reserved3 = 0
+pp_data->cap[3]->NotRange.DataIndex = 3
+pp_data->cap[3]->NotRange.Reserved4 = 3
+pp_data->cap[3]->NotButton.HasNull = 0
+pp_data->cap[3]->NotButton.Reserved4 = 0x000000
+pp_data->cap[3]->NotButton.LogicalMin = 0
+pp_data->cap[3]->NotButton.LogicalMax = 1
+pp_data->cap[3]->NotButton.PhysicalMin = 0
+pp_data->cap[3]->NotButton.PhysicalMax = 0
+pp_data->cap[3]->Units = 0
+pp_data->cap[3]->UnitsExp = 0
+
+pp_data->cap[4]->UsagePage = 0x000C
+pp_data->cap[4]->ReportID = 0x07
+pp_data->cap[4]->BitPosition = 0
+pp_data->cap[4]->BitSize = 8
+pp_data->cap[4]->ReportCount = 32
+pp_data->cap[4]->BytePosition = 0x0001
+pp_data->cap[4]->BitCount = 256
+pp_data->cap[4]->BitField = 0x02
+pp_data->cap[4]->NextBytePosition = 0x0021
+pp_data->cap[4]->LinkCollection = 0x0000
+pp_data->cap[4]->LinkUsagePage = 0x000C
+pp_data->cap[4]->LinkUsage = 0x0001
+pp_data->cap[4]->IsMultipleItemsForArray = 0
+pp_data->cap[4]->IsButtonCap = 0
+pp_data->cap[4]->IsPadding = 0
+pp_data->cap[4]->IsAbsolute = 1
+pp_data->cap[4]->IsRange = 0
+pp_data->cap[4]->IsAlias = 0
+pp_data->cap[4]->IsStringRange = 0
+pp_data->cap[4]->IsDesignatorRange = 0
+pp_data->cap[4]->Reserved1 = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[4]->NotRange.Usage = 0x0000
+pp_data->cap[4]->NotRange.Reserved1 = 0x0000
+pp_data->cap[4]->NotRange.StringIndex = 0
+pp_data->cap[4]->NotRange.Reserved2 = 0
+pp_data->cap[4]->NotRange.DesignatorIndex = 0
+pp_data->cap[4]->NotRange.Reserved3 = 0
+pp_data->cap[4]->NotRange.DataIndex = 4
+pp_data->cap[4]->NotRange.Reserved4 = 4
+pp_data->cap[4]->NotButton.HasNull = 0
+pp_data->cap[4]->NotButton.Reserved4 = 0x000000
+pp_data->cap[4]->NotButton.LogicalMin = 0
+pp_data->cap[4]->NotButton.LogicalMax = 1
+pp_data->cap[4]->NotButton.PhysicalMin = 0
+pp_data->cap[4]->NotButton.PhysicalMax = 0
+pp_data->cap[4]->Units = 0
+pp_data->cap[4]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[5]->UsagePage = 0x000C
+pp_data->cap[5]->ReportID = 0x04
+pp_data->cap[5]->BitPosition = 0
+pp_data->cap[5]->BitSize = 8
+pp_data->cap[5]->ReportCount = 36
+pp_data->cap[5]->BytePosition = 0x0001
+pp_data->cap[5]->BitCount = 288
+pp_data->cap[5]->BitField = 0x02
+pp_data->cap[5]->NextBytePosition = 0x0025
+pp_data->cap[5]->LinkCollection = 0x0000
+pp_data->cap[5]->LinkUsagePage = 0x000C
+pp_data->cap[5]->LinkUsage = 0x0001
+pp_data->cap[5]->IsMultipleItemsForArray = 0
+pp_data->cap[5]->IsButtonCap = 0
+pp_data->cap[5]->IsPadding = 0
+pp_data->cap[5]->IsAbsolute = 1
+pp_data->cap[5]->IsRange = 0
+pp_data->cap[5]->IsAlias = 0
+pp_data->cap[5]->IsStringRange = 0
+pp_data->cap[5]->IsDesignatorRange = 0
+pp_data->cap[5]->Reserved1 = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[5]->NotRange.Usage = 0x0000
+pp_data->cap[5]->NotRange.Reserved1 = 0x0000
+pp_data->cap[5]->NotRange.StringIndex = 0
+pp_data->cap[5]->NotRange.Reserved2 = 0
+pp_data->cap[5]->NotRange.DesignatorIndex = 0
+pp_data->cap[5]->NotRange.Reserved3 = 0
+pp_data->cap[5]->NotRange.DataIndex = 0
+pp_data->cap[5]->NotRange.Reserved4 = 0
+pp_data->cap[5]->NotButton.HasNull = 0
+pp_data->cap[5]->NotButton.Reserved4 = 0x000000
+pp_data->cap[5]->NotButton.LogicalMin = 0
+pp_data->cap[5]->NotButton.LogicalMax = 1
+pp_data->cap[5]->NotButton.PhysicalMin = 0
+pp_data->cap[5]->NotButton.PhysicalMax = 0
+pp_data->cap[5]->Units = 0
+pp_data->cap[5]->UnitsExp = 0
+
+pp_data->cap[6]->UsagePage = 0x000C
+pp_data->cap[6]->ReportID = 0x06
+pp_data->cap[6]->BitPosition = 0
+pp_data->cap[6]->BitSize = 8
+pp_data->cap[6]->ReportCount = 36
+pp_data->cap[6]->BytePosition = 0x0001
+pp_data->cap[6]->BitCount = 288
+pp_data->cap[6]->BitField = 0x02
+pp_data->cap[6]->NextBytePosition = 0x0025
+pp_data->cap[6]->LinkCollection = 0x0000
+pp_data->cap[6]->LinkUsagePage = 0x000C
+pp_data->cap[6]->LinkUsage = 0x0001
+pp_data->cap[6]->IsMultipleItemsForArray = 0
+pp_data->cap[6]->IsButtonCap = 0
+pp_data->cap[6]->IsPadding = 0
+pp_data->cap[6]->IsAbsolute = 1
+pp_data->cap[6]->IsRange = 0
+pp_data->cap[6]->IsAlias = 0
+pp_data->cap[6]->IsStringRange = 0
+pp_data->cap[6]->IsDesignatorRange = 0
+pp_data->cap[6]->Reserved1 = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[6]->NotRange.Usage = 0x0000
+pp_data->cap[6]->NotRange.Reserved1 = 0x0000
+pp_data->cap[6]->NotRange.StringIndex = 0
+pp_data->cap[6]->NotRange.Reserved2 = 0
+pp_data->cap[6]->NotRange.DesignatorIndex = 0
+pp_data->cap[6]->NotRange.Reserved3 = 0
+pp_data->cap[6]->NotRange.DataIndex = 1
+pp_data->cap[6]->NotRange.Reserved4 = 1
+pp_data->cap[6]->NotButton.HasNull = 0
+pp_data->cap[6]->NotButton.Reserved4 = 0x000000
+pp_data->cap[6]->NotButton.LogicalMin = 0
+pp_data->cap[6]->NotButton.LogicalMax = 1
+pp_data->cap[6]->NotButton.PhysicalMin = 0
+pp_data->cap[6]->NotButton.PhysicalMax = 0
+pp_data->cap[6]->Units = 0
+pp_data->cap[6]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x000C
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/047F_C056_0001_000C_expected.rpt_desc b/src/hidapi/windows/test/data/047F_C056_0001_000C_expected.rpt_desc
new file mode 100644
index 0000000..d7ca045
--- /dev/null
+++ b/src/hidapi/windows/test/data/047F_C056_0001_000C_expected.rpt_desc
@@ -0,0 +1,10 @@
+0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x01, 0x09, 0xE9,
+0x09, 0xEA, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x02,
+0x81, 0x06, 0x75, 0x06, 0x95, 0x01, 0x81, 0x03, 0x85, 0x02,
+0x09, 0x00, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x10,
+0x81, 0x02, 0x85, 0x05, 0x09, 0x00, 0x15, 0x00, 0x25, 0x01,
+0x75, 0x08, 0x95, 0x20, 0x81, 0x02, 0x85, 0x07, 0x09, 0x00,
+0x15, 0x00, 0x25, 0x01, 0x75, 0x08, 0x95, 0x20, 0x81, 0x02,
+0x85, 0x04, 0x09, 0x00, 0x15, 0x00, 0x25, 0x01, 0x75, 0x08,
+0x95, 0x24, 0x91, 0x02, 0x85, 0x06, 0x09, 0x00, 0x15, 0x00,
+0x25, 0x01, 0x75, 0x08, 0x95, 0x24, 0x91, 0x02, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/047F_C056_0001_000C_real.rpt_desc b/src/hidapi/windows/test/data/047F_C056_0001_000C_real.rpt_desc
new file mode 100644
index 0000000..ba0fc3a
--- /dev/null
+++ b/src/hidapi/windows/test/data/047F_C056_0001_000C_real.rpt_desc
@@ -0,0 +1,47 @@
+macOS USB Prober about 0x047F/0xC056 "Plantronics Blackwire 3220 Series"
+05 0C 09 01 A1 01 85 01 15 00 25 01 09 E9 09 EA
+75 01 95 02 81 06 95 06 81 01 85 02 05 0C 09 00
+95 10 81 02 85 04 09 00 75 08 95 24 91 02 85 05
+09 00 95 20 81 02 85 06 09 00 95 24 91 02 85 07
+09 00 95 20 81 02 C0
+
+# Parser output:
+
+0x05, 0x0C, // Usage Page (Consumer)
+0x09, 0x01, // Usage (Consumer Control)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x01, // Report ID (1)
+0x15, 0x00, // Logical Minimum (0)
+0x25, 0x01, // Logical Maximum (1)
+0x09, 0xE9, // Usage (Volume Increment)
+0x09, 0xEA, // Usage (Volume Decrement)
+0x75, 0x01, // Report Size (1)
+0x95, 0x02, // Report Count (2)
+0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+0x95, 0x06, // Report Count (6)
+0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x85, 0x02, // Report ID (2)
+0x05, 0x0C, // Usage Page (Consumer)
+0x09, 0x00, // Usage (Unassigned)
+0x95, 0x10, // Report Count (16)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x85, 0x04, // Report ID (4)
+0x09, 0x00, // Usage (Unassigned)
+0x75, 0x08, // Report Size (8)
+0x95, 0x24, // Report Count (36)
+0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x85, 0x05, // Report ID (5)
+0x09, 0x00, // Usage (Unassigned)
+0x95, 0x20, // Report Count (32)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x85, 0x06, // Report ID (6)
+0x09, 0x00, // Usage (Unassigned)
+0x95, 0x24, // Report Count (36)
+0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x85, 0x07, // Report ID (7)
+0x09, 0x00, // Usage (Unassigned)
+0x95, 0x20, // Report Count (32)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
+
+// 71 bytes
diff --git a/src/hidapi/windows/test/data/047F_C056_0003_FFA0.pp_data b/src/hidapi/windows/test/data/047F_C056_0003_FFA0.pp_data
new file mode 100644
index 0000000..6def736
--- /dev/null
+++ b/src/hidapi/windows/test/data/047F_C056_0003_FFA0.pp_data
@@ -0,0 +1,1255 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x047F
+dev->product_id = 0xC056
+dev->manufacturer_string = "Plantronics"
+dev->product_string = "Plantronics Blackwire 3220 Series"
+dev->release_number = 0x0210
+dev->interface_number = 3
+dev->usage = 0x0003
+dev->usage_page = 0xFFA0
+dev->path = "\\?\hid#vid_047f&pid_c056&mi_03&col03#f&39e6f119&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0003
+pp_data->UsagePage = 0xFFA0
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 8
+pp_data->caps_info[0]->NumberOfCaps = 8
+pp_data->caps_info[0]->ReportByteLength = 33
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 8
+pp_data->caps_info[1]->LastCap = 16
+pp_data->caps_info[1]->NumberOfCaps = 8
+pp_data->caps_info[1]->ReportByteLength = 33
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 16
+pp_data->caps_info[2]->LastCap = 26
+pp_data->caps_info[2]->NumberOfCaps = 10
+pp_data->caps_info[2]->ReportByteLength = 3
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x0A90
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0xFFA0
+pp_data->cap[0]->ReportID = 0x03
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 8
+pp_data->cap[0]->ReportCount = 32
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 256
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0021
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0xFFA0
+pp_data->cap[0]->LinkUsage = 0x0003
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 0
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0030
+pp_data->cap[0]->NotRange.Reserved1 = 0x0030
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->NotButton.HasNull = 0
+pp_data->cap[0]->NotButton.Reserved4 = 0x000000
+pp_data->cap[0]->NotButton.LogicalMin = 0
+pp_data->cap[0]->NotButton.LogicalMax = 1
+pp_data->cap[0]->NotButton.PhysicalMin = 0
+pp_data->cap[0]->NotButton.PhysicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0xFFA0
+pp_data->cap[1]->ReportID = 0x14
+pp_data->cap[1]->BitPosition = 4
+pp_data->cap[1]->BitSize = 1
+pp_data->cap[1]->ReportCount = 1
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 1
+pp_data->cap[1]->BitField = 0x06
+pp_data->cap[1]->NextBytePosition = 0x0002
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0xFFA0
+pp_data->cap[1]->LinkUsage = 0x0003
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 0
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x00B3
+pp_data->cap[1]->NotRange.Reserved1 = 0x00B3
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 1
+pp_data->cap[1]->NotRange.Reserved4 = 1
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 0
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+pp_data->cap[2]->UsagePage = 0xFFA0
+pp_data->cap[2]->ReportID = 0x14
+pp_data->cap[2]->BitPosition = 3
+pp_data->cap[2]->BitSize = 1
+pp_data->cap[2]->ReportCount = 1
+pp_data->cap[2]->BytePosition = 0x0001
+pp_data->cap[2]->BitCount = 1
+pp_data->cap[2]->BitField = 0x06
+pp_data->cap[2]->NextBytePosition = 0x0002
+pp_data->cap[2]->LinkCollection = 0x0000
+pp_data->cap[2]->LinkUsagePage = 0xFFA0
+pp_data->cap[2]->LinkUsage = 0x0003
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 1
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 0
+pp_data->cap[2]->IsRange = 0
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->NotRange.Usage = 0x00B7
+pp_data->cap[2]->NotRange.Reserved1 = 0x00B7
+pp_data->cap[2]->NotRange.StringIndex = 0
+pp_data->cap[2]->NotRange.Reserved2 = 0
+pp_data->cap[2]->NotRange.DesignatorIndex = 0
+pp_data->cap[2]->NotRange.Reserved3 = 0
+pp_data->cap[2]->NotRange.DataIndex = 2
+pp_data->cap[2]->NotRange.Reserved4 = 2
+pp_data->cap[2]->Button.LogicalMin = 0
+pp_data->cap[2]->Button.LogicalMax = 0
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+pp_data->cap[3]->UsagePage = 0xFFA0
+pp_data->cap[3]->ReportID = 0x14
+pp_data->cap[3]->BitPosition = 2
+pp_data->cap[3]->BitSize = 1
+pp_data->cap[3]->ReportCount = 1
+pp_data->cap[3]->BytePosition = 0x0001
+pp_data->cap[3]->BitCount = 1
+pp_data->cap[3]->BitField = 0x06
+pp_data->cap[3]->NextBytePosition = 0x0002
+pp_data->cap[3]->LinkCollection = 0x0000
+pp_data->cap[3]->LinkUsagePage = 0xFFA0
+pp_data->cap[3]->LinkUsage = 0x0003
+pp_data->cap[3]->IsMultipleItemsForArray = 0
+pp_data->cap[3]->IsButtonCap = 1
+pp_data->cap[3]->IsPadding = 0
+pp_data->cap[3]->IsAbsolute = 0
+pp_data->cap[3]->IsRange = 0
+pp_data->cap[3]->IsAlias = 0
+pp_data->cap[3]->IsStringRange = 0
+pp_data->cap[3]->IsDesignatorRange = 0
+pp_data->cap[3]->Reserved1 = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[3]->NotRange.Usage = 0x00B5
+pp_data->cap[3]->NotRange.Reserved1 = 0x00B5
+pp_data->cap[3]->NotRange.StringIndex = 0
+pp_data->cap[3]->NotRange.Reserved2 = 0
+pp_data->cap[3]->NotRange.DesignatorIndex = 0
+pp_data->cap[3]->NotRange.Reserved3 = 0
+pp_data->cap[3]->NotRange.DataIndex = 3
+pp_data->cap[3]->NotRange.Reserved4 = 3
+pp_data->cap[3]->Button.LogicalMin = 0
+pp_data->cap[3]->Button.LogicalMax = 0
+pp_data->cap[3]->Units = 0
+pp_data->cap[3]->UnitsExp = 0
+
+pp_data->cap[4]->UsagePage = 0xFFA0
+pp_data->cap[4]->ReportID = 0x14
+pp_data->cap[4]->BitPosition = 1
+pp_data->cap[4]->BitSize = 1
+pp_data->cap[4]->ReportCount = 1
+pp_data->cap[4]->BytePosition = 0x0001
+pp_data->cap[4]->BitCount = 1
+pp_data->cap[4]->BitField = 0x06
+pp_data->cap[4]->NextBytePosition = 0x0002
+pp_data->cap[4]->LinkCollection = 0x0000
+pp_data->cap[4]->LinkUsagePage = 0xFFA0
+pp_data->cap[4]->LinkUsage = 0x0003
+pp_data->cap[4]->IsMultipleItemsForArray = 0
+pp_data->cap[4]->IsButtonCap = 1
+pp_data->cap[4]->IsPadding = 0
+pp_data->cap[4]->IsAbsolute = 0
+pp_data->cap[4]->IsRange = 0
+pp_data->cap[4]->IsAlias = 0
+pp_data->cap[4]->IsStringRange = 0
+pp_data->cap[4]->IsDesignatorRange = 0
+pp_data->cap[4]->Reserved1 = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[4]->NotRange.Usage = 0x00B2
+pp_data->cap[4]->NotRange.Reserved1 = 0x00B2
+pp_data->cap[4]->NotRange.StringIndex = 0
+pp_data->cap[4]->NotRange.Reserved2 = 0
+pp_data->cap[4]->NotRange.DesignatorIndex = 0
+pp_data->cap[4]->NotRange.Reserved3 = 0
+pp_data->cap[4]->NotRange.DataIndex = 4
+pp_data->cap[4]->NotRange.Reserved4 = 4
+pp_data->cap[4]->Button.LogicalMin = 0
+pp_data->cap[4]->Button.LogicalMax = 0
+pp_data->cap[4]->Units = 0
+pp_data->cap[4]->UnitsExp = 0
+
+pp_data->cap[5]->UsagePage = 0xFFA0
+pp_data->cap[5]->ReportID = 0x14
+pp_data->cap[5]->BitPosition = 0
+pp_data->cap[5]->BitSize = 1
+pp_data->cap[5]->ReportCount = 1
+pp_data->cap[5]->BytePosition = 0x0001
+pp_data->cap[5]->BitCount = 1
+pp_data->cap[5]->BitField = 0x06
+pp_data->cap[5]->NextBytePosition = 0x0002
+pp_data->cap[5]->LinkCollection = 0x0000
+pp_data->cap[5]->LinkUsagePage = 0xFFA0
+pp_data->cap[5]->LinkUsage = 0x0003
+pp_data->cap[5]->IsMultipleItemsForArray = 0
+pp_data->cap[5]->IsButtonCap = 1
+pp_data->cap[5]->IsPadding = 0
+pp_data->cap[5]->IsAbsolute = 0
+pp_data->cap[5]->IsRange = 0
+pp_data->cap[5]->IsAlias = 0
+pp_data->cap[5]->IsStringRange = 0
+pp_data->cap[5]->IsDesignatorRange = 0
+pp_data->cap[5]->Reserved1 = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[5]->NotRange.Usage = 0x00B1
+pp_data->cap[5]->NotRange.Reserved1 = 0x00B1
+pp_data->cap[5]->NotRange.StringIndex = 0
+pp_data->cap[5]->NotRange.Reserved2 = 0
+pp_data->cap[5]->NotRange.DesignatorIndex = 0
+pp_data->cap[5]->NotRange.Reserved3 = 0
+pp_data->cap[5]->NotRange.DataIndex = 5
+pp_data->cap[5]->NotRange.Reserved4 = 5
+pp_data->cap[5]->Button.LogicalMin = 0
+pp_data->cap[5]->Button.LogicalMax = 0
+pp_data->cap[5]->Units = 0
+pp_data->cap[5]->UnitsExp = 0
+
+pp_data->cap[6]->UsagePage = 0xFFA0
+pp_data->cap[6]->ReportID = 0x15
+pp_data->cap[6]->BitPosition = 0
+pp_data->cap[6]->BitSize = 16
+pp_data->cap[6]->ReportCount = 1
+pp_data->cap[6]->BytePosition = 0x0001
+pp_data->cap[6]->BitCount = 16
+pp_data->cap[6]->BitField = 0x22
+pp_data->cap[6]->NextBytePosition = 0x0003
+pp_data->cap[6]->LinkCollection = 0x0000
+pp_data->cap[6]->LinkUsagePage = 0xFFA0
+pp_data->cap[6]->LinkUsage = 0x0003
+pp_data->cap[6]->IsMultipleItemsForArray = 0
+pp_data->cap[6]->IsButtonCap = 0
+pp_data->cap[6]->IsPadding = 0
+pp_data->cap[6]->IsAbsolute = 1
+pp_data->cap[6]->IsRange = 0
+pp_data->cap[6]->IsAlias = 0
+pp_data->cap[6]->IsStringRange = 0
+pp_data->cap[6]->IsDesignatorRange = 0
+pp_data->cap[6]->Reserved1 = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[6]->NotRange.Usage = 0x008C
+pp_data->cap[6]->NotRange.Reserved1 = 0x008C
+pp_data->cap[6]->NotRange.StringIndex = 0
+pp_data->cap[6]->NotRange.Reserved2 = 0
+pp_data->cap[6]->NotRange.DesignatorIndex = 0
+pp_data->cap[6]->NotRange.Reserved3 = 0
+pp_data->cap[6]->NotRange.DataIndex = 6
+pp_data->cap[6]->NotRange.Reserved4 = 6
+pp_data->cap[6]->NotButton.HasNull = 0
+pp_data->cap[6]->NotButton.Reserved4 = 0x000000
+pp_data->cap[6]->NotButton.LogicalMin = 0
+pp_data->cap[6]->NotButton.LogicalMax = 65535
+pp_data->cap[6]->NotButton.PhysicalMin = 0
+pp_data->cap[6]->NotButton.PhysicalMax = 0
+pp_data->cap[6]->Units = 0
+pp_data->cap[6]->UnitsExp = 0
+
+pp_data->cap[7]->UsagePage = 0xFFA0
+pp_data->cap[7]->ReportID = 0x1F
+pp_data->cap[7]->BitPosition = 0
+pp_data->cap[7]->BitSize = 1
+pp_data->cap[7]->ReportCount = 1
+pp_data->cap[7]->BytePosition = 0x0001
+pp_data->cap[7]->BitCount = 1
+pp_data->cap[7]->BitField = 0x06
+pp_data->cap[7]->NextBytePosition = 0x0002
+pp_data->cap[7]->LinkCollection = 0x0000
+pp_data->cap[7]->LinkUsagePage = 0xFFA0
+pp_data->cap[7]->LinkUsage = 0x0003
+pp_data->cap[7]->IsMultipleItemsForArray = 0
+pp_data->cap[7]->IsButtonCap = 1
+pp_data->cap[7]->IsPadding = 0
+pp_data->cap[7]->IsAbsolute = 0
+pp_data->cap[7]->IsRange = 0
+pp_data->cap[7]->IsAlias = 0
+pp_data->cap[7]->IsStringRange = 0
+pp_data->cap[7]->IsDesignatorRange = 0
+pp_data->cap[7]->Reserved1 = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[7]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[7]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[7]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[7]->NotRange.Usage = 0x009C
+pp_data->cap[7]->NotRange.Reserved1 = 0x009C
+pp_data->cap[7]->NotRange.StringIndex = 0
+pp_data->cap[7]->NotRange.Reserved2 = 0
+pp_data->cap[7]->NotRange.DesignatorIndex = 0
+pp_data->cap[7]->NotRange.Reserved3 = 0
+pp_data->cap[7]->NotRange.DataIndex = 7
+pp_data->cap[7]->NotRange.Reserved4 = 7
+pp_data->cap[7]->Button.LogicalMin = 0
+pp_data->cap[7]->Button.LogicalMax = 0
+pp_data->cap[7]->Units = 0
+pp_data->cap[7]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[8]->UsagePage = 0xFFA0
+pp_data->cap[8]->ReportID = 0x03
+pp_data->cap[8]->BitPosition = 0
+pp_data->cap[8]->BitSize = 8
+pp_data->cap[8]->ReportCount = 32
+pp_data->cap[8]->BytePosition = 0x0001
+pp_data->cap[8]->BitCount = 256
+pp_data->cap[8]->BitField = 0x02
+pp_data->cap[8]->NextBytePosition = 0x0021
+pp_data->cap[8]->LinkCollection = 0x0000
+pp_data->cap[8]->LinkUsagePage = 0xFFA0
+pp_data->cap[8]->LinkUsage = 0x0003
+pp_data->cap[8]->IsMultipleItemsForArray = 0
+pp_data->cap[8]->IsButtonCap = 0
+pp_data->cap[8]->IsPadding = 0
+pp_data->cap[8]->IsAbsolute = 1
+pp_data->cap[8]->IsRange = 0
+pp_data->cap[8]->IsAlias = 0
+pp_data->cap[8]->IsStringRange = 0
+pp_data->cap[8]->IsDesignatorRange = 0
+pp_data->cap[8]->Reserved1 = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[8]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[8]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[8]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[8]->NotRange.Usage = 0x0030
+pp_data->cap[8]->NotRange.Reserved1 = 0x0030
+pp_data->cap[8]->NotRange.StringIndex = 0
+pp_data->cap[8]->NotRange.Reserved2 = 0
+pp_data->cap[8]->NotRange.DesignatorIndex = 0
+pp_data->cap[8]->NotRange.Reserved3 = 0
+pp_data->cap[8]->NotRange.DataIndex = 0
+pp_data->cap[8]->NotRange.Reserved4 = 0
+pp_data->cap[8]->NotButton.HasNull = 0
+pp_data->cap[8]->NotButton.Reserved4 = 0x000000
+pp_data->cap[8]->NotButton.LogicalMin = 0
+pp_data->cap[8]->NotButton.LogicalMax = 1
+pp_data->cap[8]->NotButton.PhysicalMin = 0
+pp_data->cap[8]->NotButton.PhysicalMax = 0
+pp_data->cap[8]->Units = 0
+pp_data->cap[8]->UnitsExp = 0
+
+pp_data->cap[9]->UsagePage = 0xFFA0
+pp_data->cap[9]->ReportID = 0x19
+pp_data->cap[9]->BitPosition = 3
+pp_data->cap[9]->BitSize = 1
+pp_data->cap[9]->ReportCount = 1
+pp_data->cap[9]->BytePosition = 0x0001
+pp_data->cap[9]->BitCount = 1
+pp_data->cap[9]->BitField = 0x22
+pp_data->cap[9]->NextBytePosition = 0x0002
+pp_data->cap[9]->LinkCollection = 0x0000
+pp_data->cap[9]->LinkUsagePage = 0xFFA0
+pp_data->cap[9]->LinkUsage = 0x0003
+pp_data->cap[9]->IsMultipleItemsForArray = 0
+pp_data->cap[9]->IsButtonCap = 1
+pp_data->cap[9]->IsPadding = 0
+pp_data->cap[9]->IsAbsolute = 1
+pp_data->cap[9]->IsRange = 0
+pp_data->cap[9]->IsAlias = 0
+pp_data->cap[9]->IsStringRange = 0
+pp_data->cap[9]->IsDesignatorRange = 0
+pp_data->cap[9]->Reserved1 = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[9]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[9]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[9]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[9]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[9]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[9]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[9]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[9]->NotRange.Usage = 0x00DC
+pp_data->cap[9]->NotRange.Reserved1 = 0x00DC
+pp_data->cap[9]->NotRange.StringIndex = 0
+pp_data->cap[9]->NotRange.Reserved2 = 0
+pp_data->cap[9]->NotRange.DesignatorIndex = 0
+pp_data->cap[9]->NotRange.Reserved3 = 0
+pp_data->cap[9]->NotRange.DataIndex = 1
+pp_data->cap[9]->NotRange.Reserved4 = 1
+pp_data->cap[9]->Button.LogicalMin = 0
+pp_data->cap[9]->Button.LogicalMax = 0
+pp_data->cap[9]->Units = 0
+pp_data->cap[9]->UnitsExp = 0
+
+pp_data->cap[10]->UsagePage = 0xFFA0
+pp_data->cap[10]->ReportID = 0x19
+pp_data->cap[10]->BitPosition = 2
+pp_data->cap[10]->BitSize = 1
+pp_data->cap[10]->ReportCount = 1
+pp_data->cap[10]->BytePosition = 0x0001
+pp_data->cap[10]->BitCount = 1
+pp_data->cap[10]->BitField = 0x22
+pp_data->cap[10]->NextBytePosition = 0x0002
+pp_data->cap[10]->LinkCollection = 0x0000
+pp_data->cap[10]->LinkUsagePage = 0xFFA0
+pp_data->cap[10]->LinkUsage = 0x0003
+pp_data->cap[10]->IsMultipleItemsForArray = 0
+pp_data->cap[10]->IsButtonCap = 1
+pp_data->cap[10]->IsPadding = 0
+pp_data->cap[10]->IsAbsolute = 1
+pp_data->cap[10]->IsRange = 0
+pp_data->cap[10]->IsAlias = 0
+pp_data->cap[10]->IsStringRange = 0
+pp_data->cap[10]->IsDesignatorRange = 0
+pp_data->cap[10]->Reserved1 = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[10]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[10]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[10]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[10]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[10]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[10]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[10]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[10]->NotRange.Usage = 0x009E
+pp_data->cap[10]->NotRange.Reserved1 = 0x009E
+pp_data->cap[10]->NotRange.StringIndex = 0
+pp_data->cap[10]->NotRange.Reserved2 = 0
+pp_data->cap[10]->NotRange.DesignatorIndex = 0
+pp_data->cap[10]->NotRange.Reserved3 = 0
+pp_data->cap[10]->NotRange.DataIndex = 2
+pp_data->cap[10]->NotRange.Reserved4 = 2
+pp_data->cap[10]->Button.LogicalMin = 0
+pp_data->cap[10]->Button.LogicalMax = 0
+pp_data->cap[10]->Units = 0
+pp_data->cap[10]->UnitsExp = 0
+
+pp_data->cap[11]->UsagePage = 0xFFA0
+pp_data->cap[11]->ReportID = 0x19
+pp_data->cap[11]->BitPosition = 1
+pp_data->cap[11]->BitSize = 1
+pp_data->cap[11]->ReportCount = 1
+pp_data->cap[11]->BytePosition = 0x0001
+pp_data->cap[11]->BitCount = 1
+pp_data->cap[11]->BitField = 0x22
+pp_data->cap[11]->NextBytePosition = 0x0002
+pp_data->cap[11]->LinkCollection = 0x0000
+pp_data->cap[11]->LinkUsagePage = 0xFFA0
+pp_data->cap[11]->LinkUsage = 0x0003
+pp_data->cap[11]->IsMultipleItemsForArray = 0
+pp_data->cap[11]->IsButtonCap = 1
+pp_data->cap[11]->IsPadding = 0
+pp_data->cap[11]->IsAbsolute = 1
+pp_data->cap[11]->IsRange = 0
+pp_data->cap[11]->IsAlias = 0
+pp_data->cap[11]->IsStringRange = 0
+pp_data->cap[11]->IsDesignatorRange = 0
+pp_data->cap[11]->Reserved1 = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[11]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[11]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[11]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[11]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[11]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[11]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[11]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[11]->NotRange.Usage = 0x008F
+pp_data->cap[11]->NotRange.Reserved1 = 0x008F
+pp_data->cap[11]->NotRange.StringIndex = 0
+pp_data->cap[11]->NotRange.Reserved2 = 0
+pp_data->cap[11]->NotRange.DesignatorIndex = 0
+pp_data->cap[11]->NotRange.Reserved3 = 0
+pp_data->cap[11]->NotRange.DataIndex = 3
+pp_data->cap[11]->NotRange.Reserved4 = 3
+pp_data->cap[11]->Button.LogicalMin = 0
+pp_data->cap[11]->Button.LogicalMax = 0
+pp_data->cap[11]->Units = 0
+pp_data->cap[11]->UnitsExp = 0
+
+pp_data->cap[12]->UsagePage = 0xFFA0
+pp_data->cap[12]->ReportID = 0x19
+pp_data->cap[12]->BitPosition = 0
+pp_data->cap[12]->BitSize = 1
+pp_data->cap[12]->ReportCount = 1
+pp_data->cap[12]->BytePosition = 0x0001
+pp_data->cap[12]->BitCount = 1
+pp_data->cap[12]->BitField = 0x22
+pp_data->cap[12]->NextBytePosition = 0x0002
+pp_data->cap[12]->LinkCollection = 0x0000
+pp_data->cap[12]->LinkUsagePage = 0xFFA0
+pp_data->cap[12]->LinkUsage = 0x0003
+pp_data->cap[12]->IsMultipleItemsForArray = 0
+pp_data->cap[12]->IsButtonCap = 1
+pp_data->cap[12]->IsPadding = 0
+pp_data->cap[12]->IsAbsolute = 1
+pp_data->cap[12]->IsRange = 0
+pp_data->cap[12]->IsAlias = 0
+pp_data->cap[12]->IsStringRange = 0
+pp_data->cap[12]->IsDesignatorRange = 0
+pp_data->cap[12]->Reserved1 = 0x000000
+pp_data->cap[12]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[12]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[12]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[12]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[12]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[12]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[12]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[12]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[12]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[12]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[12]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[12]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[12]->NotRange.Usage = 0x008D
+pp_data->cap[12]->NotRange.Reserved1 = 0x008D
+pp_data->cap[12]->NotRange.StringIndex = 0
+pp_data->cap[12]->NotRange.Reserved2 = 0
+pp_data->cap[12]->NotRange.DesignatorIndex = 0
+pp_data->cap[12]->NotRange.Reserved3 = 0
+pp_data->cap[12]->NotRange.DataIndex = 4
+pp_data->cap[12]->NotRange.Reserved4 = 4
+pp_data->cap[12]->Button.LogicalMin = 0
+pp_data->cap[12]->Button.LogicalMax = 0
+pp_data->cap[12]->Units = 0
+pp_data->cap[12]->UnitsExp = 0
+
+pp_data->cap[13]->UsagePage = 0xFFA0
+pp_data->cap[13]->ReportID = 0x19
+pp_data->cap[13]->BitPosition = 5
+pp_data->cap[13]->BitSize = 1
+pp_data->cap[13]->ReportCount = 1
+pp_data->cap[13]->BytePosition = 0x0001
+pp_data->cap[13]->BitCount = 1
+pp_data->cap[13]->BitField = 0x06
+pp_data->cap[13]->NextBytePosition = 0x0002
+pp_data->cap[13]->LinkCollection = 0x0000
+pp_data->cap[13]->LinkUsagePage = 0xFFA0
+pp_data->cap[13]->LinkUsage = 0x0003
+pp_data->cap[13]->IsMultipleItemsForArray = 0
+pp_data->cap[13]->IsButtonCap = 1
+pp_data->cap[13]->IsPadding = 0
+pp_data->cap[13]->IsAbsolute = 0
+pp_data->cap[13]->IsRange = 0
+pp_data->cap[13]->IsAlias = 0
+pp_data->cap[13]->IsStringRange = 0
+pp_data->cap[13]->IsDesignatorRange = 0
+pp_data->cap[13]->Reserved1 = 0x000000
+pp_data->cap[13]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[13]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[13]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[13]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[13]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[13]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[13]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[13]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[13]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[13]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[13]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[13]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[13]->NotRange.Usage = 0x00D9
+pp_data->cap[13]->NotRange.Reserved1 = 0x00D9
+pp_data->cap[13]->NotRange.StringIndex = 0
+pp_data->cap[13]->NotRange.Reserved2 = 0
+pp_data->cap[13]->NotRange.DesignatorIndex = 0
+pp_data->cap[13]->NotRange.Reserved3 = 0
+pp_data->cap[13]->NotRange.DataIndex = 5
+pp_data->cap[13]->NotRange.Reserved4 = 5
+pp_data->cap[13]->Button.LogicalMin = 0
+pp_data->cap[13]->Button.LogicalMax = 0
+pp_data->cap[13]->Units = 0
+pp_data->cap[13]->UnitsExp = 0
+
+pp_data->cap[14]->UsagePage = 0xFFA0
+pp_data->cap[14]->ReportID = 0x19
+pp_data->cap[14]->BitPosition = 4
+pp_data->cap[14]->BitSize = 1
+pp_data->cap[14]->ReportCount = 1
+pp_data->cap[14]->BytePosition = 0x0001
+pp_data->cap[14]->BitCount = 1
+pp_data->cap[14]->BitField = 0x06
+pp_data->cap[14]->NextBytePosition = 0x0002
+pp_data->cap[14]->LinkCollection = 0x0000
+pp_data->cap[14]->LinkUsagePage = 0xFFA0
+pp_data->cap[14]->LinkUsage = 0x0003
+pp_data->cap[14]->IsMultipleItemsForArray = 0
+pp_data->cap[14]->IsButtonCap = 1
+pp_data->cap[14]->IsPadding = 0
+pp_data->cap[14]->IsAbsolute = 0
+pp_data->cap[14]->IsRange = 0
+pp_data->cap[14]->IsAlias = 0
+pp_data->cap[14]->IsStringRange = 0
+pp_data->cap[14]->IsDesignatorRange = 0
+pp_data->cap[14]->Reserved1 = 0x000000
+pp_data->cap[14]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[14]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[14]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[14]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[14]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[14]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[14]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[14]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[14]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[14]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[14]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[14]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[14]->NotRange.Usage = 0x00D2
+pp_data->cap[14]->NotRange.Reserved1 = 0x00D2
+pp_data->cap[14]->NotRange.StringIndex = 0
+pp_data->cap[14]->NotRange.Reserved2 = 0
+pp_data->cap[14]->NotRange.DesignatorIndex = 0
+pp_data->cap[14]->NotRange.Reserved3 = 0
+pp_data->cap[14]->NotRange.DataIndex = 6
+pp_data->cap[14]->NotRange.Reserved4 = 6
+pp_data->cap[14]->Button.LogicalMin = 0
+pp_data->cap[14]->Button.LogicalMax = 0
+pp_data->cap[14]->Units = 0
+pp_data->cap[14]->UnitsExp = 0
+
+pp_data->cap[15]->UsagePage = 0xFFA0
+pp_data->cap[15]->ReportID = 0x1A
+pp_data->cap[15]->BitPosition = 0
+pp_data->cap[15]->BitSize = 1
+pp_data->cap[15]->ReportCount = 1
+pp_data->cap[15]->BytePosition = 0x0001
+pp_data->cap[15]->BitCount = 1
+pp_data->cap[15]->BitField = 0x22
+pp_data->cap[15]->NextBytePosition = 0x0002
+pp_data->cap[15]->LinkCollection = 0x0000
+pp_data->cap[15]->LinkUsagePage = 0xFFA0
+pp_data->cap[15]->LinkUsage = 0x0003
+pp_data->cap[15]->IsMultipleItemsForArray = 0
+pp_data->cap[15]->IsButtonCap = 1
+pp_data->cap[15]->IsPadding = 0
+pp_data->cap[15]->IsAbsolute = 1
+pp_data->cap[15]->IsRange = 0
+pp_data->cap[15]->IsAlias = 0
+pp_data->cap[15]->IsStringRange = 0
+pp_data->cap[15]->IsDesignatorRange = 0
+pp_data->cap[15]->Reserved1 = 0x000000
+pp_data->cap[15]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[15]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[15]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[15]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[15]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[15]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[15]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[15]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[15]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[15]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[15]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[15]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[15]->NotRange.Usage = 0x00B5
+pp_data->cap[15]->NotRange.Reserved1 = 0x00B5
+pp_data->cap[15]->NotRange.StringIndex = 0
+pp_data->cap[15]->NotRange.Reserved2 = 0
+pp_data->cap[15]->NotRange.DesignatorIndex = 0
+pp_data->cap[15]->NotRange.Reserved3 = 0
+pp_data->cap[15]->NotRange.DataIndex = 7
+pp_data->cap[15]->NotRange.Reserved4 = 7
+pp_data->cap[15]->Button.LogicalMin = 0
+pp_data->cap[15]->Button.LogicalMax = 0
+pp_data->cap[15]->Units = 0
+pp_data->cap[15]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+pp_data->cap[16]->UsagePage = 0xFFA0
+pp_data->cap[16]->ReportID = 0x1B
+pp_data->cap[16]->BitPosition = 1
+pp_data->cap[16]->BitSize = 1
+pp_data->cap[16]->ReportCount = 1
+pp_data->cap[16]->BytePosition = 0x0001
+pp_data->cap[16]->BitCount = 1
+pp_data->cap[16]->BitField = 0x22
+pp_data->cap[16]->NextBytePosition = 0x0002
+pp_data->cap[16]->LinkCollection = 0x0000
+pp_data->cap[16]->LinkUsagePage = 0xFFA0
+pp_data->cap[16]->LinkUsage = 0x0003
+pp_data->cap[16]->IsMultipleItemsForArray = 0
+pp_data->cap[16]->IsButtonCap = 1
+pp_data->cap[16]->IsPadding = 0
+pp_data->cap[16]->IsAbsolute = 1
+pp_data->cap[16]->IsRange = 0
+pp_data->cap[16]->IsAlias = 0
+pp_data->cap[16]->IsStringRange = 0
+pp_data->cap[16]->IsDesignatorRange = 0
+pp_data->cap[16]->Reserved1 = 0x000000
+pp_data->cap[16]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[16]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[16]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[16]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[16]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[16]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[16]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[16]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[16]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[16]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[16]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[16]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[16]->NotRange.Usage = 0x00B5
+pp_data->cap[16]->NotRange.Reserved1 = 0x00B5
+pp_data->cap[16]->NotRange.StringIndex = 0
+pp_data->cap[16]->NotRange.Reserved2 = 0
+pp_data->cap[16]->NotRange.DesignatorIndex = 0
+pp_data->cap[16]->NotRange.Reserved3 = 0
+pp_data->cap[16]->NotRange.DataIndex = 0
+pp_data->cap[16]->NotRange.Reserved4 = 0
+pp_data->cap[16]->Button.LogicalMin = 0
+pp_data->cap[16]->Button.LogicalMax = 0
+pp_data->cap[16]->Units = 0
+pp_data->cap[16]->UnitsExp = 0
+
+pp_data->cap[17]->UsagePage = 0xFFA0
+pp_data->cap[17]->ReportID = 0x1B
+pp_data->cap[17]->BitPosition = 0
+pp_data->cap[17]->BitSize = 1
+pp_data->cap[17]->ReportCount = 1
+pp_data->cap[17]->BytePosition = 0x0001
+pp_data->cap[17]->BitCount = 1
+pp_data->cap[17]->BitField = 0x22
+pp_data->cap[17]->NextBytePosition = 0x0002
+pp_data->cap[17]->LinkCollection = 0x0000
+pp_data->cap[17]->LinkUsagePage = 0xFFA0
+pp_data->cap[17]->LinkUsage = 0x0003
+pp_data->cap[17]->IsMultipleItemsForArray = 0
+pp_data->cap[17]->IsButtonCap = 1
+pp_data->cap[17]->IsPadding = 0
+pp_data->cap[17]->IsAbsolute = 1
+pp_data->cap[17]->IsRange = 0
+pp_data->cap[17]->IsAlias = 0
+pp_data->cap[17]->IsStringRange = 0
+pp_data->cap[17]->IsDesignatorRange = 0
+pp_data->cap[17]->Reserved1 = 0x000000
+pp_data->cap[17]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[17]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[17]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[17]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[17]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[17]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[17]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[17]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[17]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[17]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[17]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[17]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[17]->NotRange.Usage = 0x00CF
+pp_data->cap[17]->NotRange.Reserved1 = 0x00CF
+pp_data->cap[17]->NotRange.StringIndex = 0
+pp_data->cap[17]->NotRange.Reserved2 = 0
+pp_data->cap[17]->NotRange.DesignatorIndex = 0
+pp_data->cap[17]->NotRange.Reserved3 = 0
+pp_data->cap[17]->NotRange.DataIndex = 1
+pp_data->cap[17]->NotRange.Reserved4 = 1
+pp_data->cap[17]->Button.LogicalMin = 0
+pp_data->cap[17]->Button.LogicalMax = 0
+pp_data->cap[17]->Units = 0
+pp_data->cap[17]->UnitsExp = 0
+
+pp_data->cap[18]->UsagePage = 0xFFA0
+pp_data->cap[18]->ReportID = 0x1B
+pp_data->cap[18]->BitPosition = 2
+pp_data->cap[18]->BitSize = 1
+pp_data->cap[18]->ReportCount = 1
+pp_data->cap[18]->BytePosition = 0x0001
+pp_data->cap[18]->BitCount = 1
+pp_data->cap[18]->BitField = 0x23
+pp_data->cap[18]->NextBytePosition = 0x0002
+pp_data->cap[18]->LinkCollection = 0x0000
+pp_data->cap[18]->LinkUsagePage = 0xFFA0
+pp_data->cap[18]->LinkUsage = 0x0003
+pp_data->cap[18]->IsMultipleItemsForArray = 0
+pp_data->cap[18]->IsButtonCap = 1
+pp_data->cap[18]->IsPadding = 1
+pp_data->cap[18]->IsAbsolute = 1
+pp_data->cap[18]->IsRange = 0
+pp_data->cap[18]->IsAlias = 0
+pp_data->cap[18]->IsStringRange = 0
+pp_data->cap[18]->IsDesignatorRange = 0
+pp_data->cap[18]->Reserved1 = 0x000000
+pp_data->cap[18]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[18]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[18]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[18]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[18]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[18]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[18]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[18]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[18]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[18]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[18]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[18]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[18]->NotRange.Usage = 0x00DE
+pp_data->cap[18]->NotRange.Reserved1 = 0x00DE
+pp_data->cap[18]->NotRange.StringIndex = 0
+pp_data->cap[18]->NotRange.Reserved2 = 0
+pp_data->cap[18]->NotRange.DesignatorIndex = 0
+pp_data->cap[18]->NotRange.Reserved3 = 0
+pp_data->cap[18]->NotRange.DataIndex = 2
+pp_data->cap[18]->NotRange.Reserved4 = 2
+pp_data->cap[18]->Button.LogicalMin = 0
+pp_data->cap[18]->Button.LogicalMax = 0
+pp_data->cap[18]->Units = 0
+pp_data->cap[18]->UnitsExp = 0
+
+pp_data->cap[19]->UsagePage = 0xFFA0
+pp_data->cap[19]->ReportID = 0x1B
+pp_data->cap[19]->BitPosition = 3
+pp_data->cap[19]->BitSize = 1
+pp_data->cap[19]->ReportCount = 1
+pp_data->cap[19]->BytePosition = 0x0001
+pp_data->cap[19]->BitCount = 1
+pp_data->cap[19]->BitField = 0x22
+pp_data->cap[19]->NextBytePosition = 0x0002
+pp_data->cap[19]->LinkCollection = 0x0000
+pp_data->cap[19]->LinkUsagePage = 0xFFA0
+pp_data->cap[19]->LinkUsage = 0x0003
+pp_data->cap[19]->IsMultipleItemsForArray = 0
+pp_data->cap[19]->IsButtonCap = 1
+pp_data->cap[19]->IsPadding = 0
+pp_data->cap[19]->IsAbsolute = 1
+pp_data->cap[19]->IsRange = 0
+pp_data->cap[19]->IsAlias = 0
+pp_data->cap[19]->IsStringRange = 0
+pp_data->cap[19]->IsDesignatorRange = 0
+pp_data->cap[19]->Reserved1 = 0x000000
+pp_data->cap[19]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[19]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[19]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[19]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[19]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[19]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[19]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[19]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[19]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[19]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[19]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[19]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[19]->NotRange.Usage = 0x00D8
+pp_data->cap[19]->NotRange.Reserved1 = 0x00D8
+pp_data->cap[19]->NotRange.StringIndex = 0
+pp_data->cap[19]->NotRange.Reserved2 = 0
+pp_data->cap[19]->NotRange.DesignatorIndex = 0
+pp_data->cap[19]->NotRange.Reserved3 = 0
+pp_data->cap[19]->NotRange.DataIndex = 3
+pp_data->cap[19]->NotRange.Reserved4 = 3
+pp_data->cap[19]->Button.LogicalMin = 0
+pp_data->cap[19]->Button.LogicalMax = 0
+pp_data->cap[19]->Units = 0
+pp_data->cap[19]->UnitsExp = 0
+
+pp_data->cap[20]->UsagePage = 0xFFA0
+pp_data->cap[20]->ReportID = 0x1B
+pp_data->cap[20]->BitPosition = 5
+pp_data->cap[20]->BitSize = 1
+pp_data->cap[20]->ReportCount = 1
+pp_data->cap[20]->BytePosition = 0x0002
+pp_data->cap[20]->BitCount = 1
+pp_data->cap[20]->BitField = 0x22
+pp_data->cap[20]->NextBytePosition = 0x0003
+pp_data->cap[20]->LinkCollection = 0x0000
+pp_data->cap[20]->LinkUsagePage = 0xFFA0
+pp_data->cap[20]->LinkUsage = 0x0003
+pp_data->cap[20]->IsMultipleItemsForArray = 0
+pp_data->cap[20]->IsButtonCap = 1
+pp_data->cap[20]->IsPadding = 0
+pp_data->cap[20]->IsAbsolute = 1
+pp_data->cap[20]->IsRange = 0
+pp_data->cap[20]->IsAlias = 0
+pp_data->cap[20]->IsStringRange = 0
+pp_data->cap[20]->IsDesignatorRange = 0
+pp_data->cap[20]->Reserved1 = 0x000000
+pp_data->cap[20]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[20]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[20]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[20]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[20]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[20]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[20]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[20]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[20]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[20]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[20]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[20]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[20]->NotRange.Usage = 0x002A
+pp_data->cap[20]->NotRange.Reserved1 = 0x002A
+pp_data->cap[20]->NotRange.StringIndex = 0
+pp_data->cap[20]->NotRange.Reserved2 = 0
+pp_data->cap[20]->NotRange.DesignatorIndex = 0
+pp_data->cap[20]->NotRange.Reserved3 = 0
+pp_data->cap[20]->NotRange.DataIndex = 4
+pp_data->cap[20]->NotRange.Reserved4 = 4
+pp_data->cap[20]->Button.LogicalMin = 0
+pp_data->cap[20]->Button.LogicalMax = 0
+pp_data->cap[20]->Units = 0
+pp_data->cap[20]->UnitsExp = 0
+
+pp_data->cap[21]->UsagePage = 0xFFA0
+pp_data->cap[21]->ReportID = 0x1B
+pp_data->cap[21]->BitPosition = 4
+pp_data->cap[21]->BitSize = 1
+pp_data->cap[21]->ReportCount = 1
+pp_data->cap[21]->BytePosition = 0x0002
+pp_data->cap[21]->BitCount = 1
+pp_data->cap[21]->BitField = 0x22
+pp_data->cap[21]->NextBytePosition = 0x0003
+pp_data->cap[21]->LinkCollection = 0x0000
+pp_data->cap[21]->LinkUsagePage = 0xFFA0
+pp_data->cap[21]->LinkUsage = 0x0003
+pp_data->cap[21]->IsMultipleItemsForArray = 0
+pp_data->cap[21]->IsButtonCap = 1
+pp_data->cap[21]->IsPadding = 0
+pp_data->cap[21]->IsAbsolute = 1
+pp_data->cap[21]->IsRange = 0
+pp_data->cap[21]->IsAlias = 0
+pp_data->cap[21]->IsStringRange = 0
+pp_data->cap[21]->IsDesignatorRange = 0
+pp_data->cap[21]->Reserved1 = 0x000000
+pp_data->cap[21]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[21]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[21]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[21]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[21]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[21]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[21]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[21]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[21]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[21]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[21]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[21]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[21]->NotRange.Usage = 0x0020
+pp_data->cap[21]->NotRange.Reserved1 = 0x0020
+pp_data->cap[21]->NotRange.StringIndex = 0
+pp_data->cap[21]->NotRange.Reserved2 = 0
+pp_data->cap[21]->NotRange.DesignatorIndex = 0
+pp_data->cap[21]->NotRange.Reserved3 = 0
+pp_data->cap[21]->NotRange.DataIndex = 5
+pp_data->cap[21]->NotRange.Reserved4 = 5
+pp_data->cap[21]->Button.LogicalMin = 0
+pp_data->cap[21]->Button.LogicalMax = 0
+pp_data->cap[21]->Units = 0
+pp_data->cap[21]->UnitsExp = 0
+
+pp_data->cap[22]->UsagePage = 0xFFA0
+pp_data->cap[22]->ReportID = 0x1B
+pp_data->cap[22]->BitPosition = 3
+pp_data->cap[22]->BitSize = 1
+pp_data->cap[22]->ReportCount = 1
+pp_data->cap[22]->BytePosition = 0x0002
+pp_data->cap[22]->BitCount = 1
+pp_data->cap[22]->BitField = 0x22
+pp_data->cap[22]->NextBytePosition = 0x0003
+pp_data->cap[22]->LinkCollection = 0x0000
+pp_data->cap[22]->LinkUsagePage = 0xFFA0
+pp_data->cap[22]->LinkUsage = 0x0003
+pp_data->cap[22]->IsMultipleItemsForArray = 0
+pp_data->cap[22]->IsButtonCap = 1
+pp_data->cap[22]->IsPadding = 0
+pp_data->cap[22]->IsAbsolute = 1
+pp_data->cap[22]->IsRange = 0
+pp_data->cap[22]->IsAlias = 0
+pp_data->cap[22]->IsStringRange = 0
+pp_data->cap[22]->IsDesignatorRange = 0
+pp_data->cap[22]->Reserved1 = 0x000000
+pp_data->cap[22]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[22]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[22]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[22]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[22]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[22]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[22]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[22]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[22]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[22]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[22]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[22]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[22]->NotRange.Usage = 0x001E
+pp_data->cap[22]->NotRange.Reserved1 = 0x001E
+pp_data->cap[22]->NotRange.StringIndex = 0
+pp_data->cap[22]->NotRange.Reserved2 = 0
+pp_data->cap[22]->NotRange.DesignatorIndex = 0
+pp_data->cap[22]->NotRange.Reserved3 = 0
+pp_data->cap[22]->NotRange.DataIndex = 6
+pp_data->cap[22]->NotRange.Reserved4 = 6
+pp_data->cap[22]->Button.LogicalMin = 0
+pp_data->cap[22]->Button.LogicalMax = 0
+pp_data->cap[22]->Units = 0
+pp_data->cap[22]->UnitsExp = 0
+
+pp_data->cap[23]->UsagePage = 0xFFA0
+pp_data->cap[23]->ReportID = 0x1B
+pp_data->cap[23]->BitPosition = 2
+pp_data->cap[23]->BitSize = 1
+pp_data->cap[23]->ReportCount = 1
+pp_data->cap[23]->BytePosition = 0x0002
+pp_data->cap[23]->BitCount = 1
+pp_data->cap[23]->BitField = 0x22
+pp_data->cap[23]->NextBytePosition = 0x0003
+pp_data->cap[23]->LinkCollection = 0x0000
+pp_data->cap[23]->LinkUsagePage = 0xFFA0
+pp_data->cap[23]->LinkUsage = 0x0003
+pp_data->cap[23]->IsMultipleItemsForArray = 0
+pp_data->cap[23]->IsButtonCap = 1
+pp_data->cap[23]->IsPadding = 0
+pp_data->cap[23]->IsAbsolute = 1
+pp_data->cap[23]->IsRange = 0
+pp_data->cap[23]->IsAlias = 0
+pp_data->cap[23]->IsStringRange = 0
+pp_data->cap[23]->IsDesignatorRange = 0
+pp_data->cap[23]->Reserved1 = 0x000000
+pp_data->cap[23]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[23]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[23]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[23]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[23]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[23]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[23]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[23]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[23]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[23]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[23]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[23]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[23]->NotRange.Usage = 0x0018
+pp_data->cap[23]->NotRange.Reserved1 = 0x0018
+pp_data->cap[23]->NotRange.StringIndex = 0
+pp_data->cap[23]->NotRange.Reserved2 = 0
+pp_data->cap[23]->NotRange.DesignatorIndex = 0
+pp_data->cap[23]->NotRange.Reserved3 = 0
+pp_data->cap[23]->NotRange.DataIndex = 7
+pp_data->cap[23]->NotRange.Reserved4 = 7
+pp_data->cap[23]->Button.LogicalMin = 0
+pp_data->cap[23]->Button.LogicalMax = 0
+pp_data->cap[23]->Units = 0
+pp_data->cap[23]->UnitsExp = 0
+
+pp_data->cap[24]->UsagePage = 0xFFA0
+pp_data->cap[24]->ReportID = 0x1B
+pp_data->cap[24]->BitPosition = 1
+pp_data->cap[24]->BitSize = 1
+pp_data->cap[24]->ReportCount = 1
+pp_data->cap[24]->BytePosition = 0x0002
+pp_data->cap[24]->BitCount = 1
+pp_data->cap[24]->BitField = 0x22
+pp_data->cap[24]->NextBytePosition = 0x0003
+pp_data->cap[24]->LinkCollection = 0x0000
+pp_data->cap[24]->LinkUsagePage = 0xFFA0
+pp_data->cap[24]->LinkUsage = 0x0003
+pp_data->cap[24]->IsMultipleItemsForArray = 0
+pp_data->cap[24]->IsButtonCap = 1
+pp_data->cap[24]->IsPadding = 0
+pp_data->cap[24]->IsAbsolute = 1
+pp_data->cap[24]->IsRange = 0
+pp_data->cap[24]->IsAlias = 0
+pp_data->cap[24]->IsStringRange = 0
+pp_data->cap[24]->IsDesignatorRange = 0
+pp_data->cap[24]->Reserved1 = 0x000000
+pp_data->cap[24]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[24]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[24]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[24]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[24]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[24]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[24]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[24]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[24]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[24]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[24]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[24]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[24]->NotRange.Usage = 0x0017
+pp_data->cap[24]->NotRange.Reserved1 = 0x0017
+pp_data->cap[24]->NotRange.StringIndex = 0
+pp_data->cap[24]->NotRange.Reserved2 = 0
+pp_data->cap[24]->NotRange.DesignatorIndex = 0
+pp_data->cap[24]->NotRange.Reserved3 = 0
+pp_data->cap[24]->NotRange.DataIndex = 8
+pp_data->cap[24]->NotRange.Reserved4 = 8
+pp_data->cap[24]->Button.LogicalMin = 0
+pp_data->cap[24]->Button.LogicalMax = 0
+pp_data->cap[24]->Units = 0
+pp_data->cap[24]->UnitsExp = 0
+
+pp_data->cap[25]->UsagePage = 0xFFA0
+pp_data->cap[25]->ReportID = 0x1B
+pp_data->cap[25]->BitPosition = 0
+pp_data->cap[25]->BitSize = 1
+pp_data->cap[25]->ReportCount = 1
+pp_data->cap[25]->BytePosition = 0x0002
+pp_data->cap[25]->BitCount = 1
+pp_data->cap[25]->BitField = 0x22
+pp_data->cap[25]->NextBytePosition = 0x0003
+pp_data->cap[25]->LinkCollection = 0x0000
+pp_data->cap[25]->LinkUsagePage = 0xFFA0
+pp_data->cap[25]->LinkUsage = 0x0003
+pp_data->cap[25]->IsMultipleItemsForArray = 0
+pp_data->cap[25]->IsButtonCap = 1
+pp_data->cap[25]->IsPadding = 0
+pp_data->cap[25]->IsAbsolute = 1
+pp_data->cap[25]->IsRange = 0
+pp_data->cap[25]->IsAlias = 0
+pp_data->cap[25]->IsStringRange = 0
+pp_data->cap[25]->IsDesignatorRange = 0
+pp_data->cap[25]->Reserved1 = 0x000000
+pp_data->cap[25]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[25]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[25]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[25]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[25]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[25]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[25]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[25]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[25]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[25]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[25]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[25]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[25]->NotRange.Usage = 0x0009
+pp_data->cap[25]->NotRange.Reserved1 = 0x0009
+pp_data->cap[25]->NotRange.StringIndex = 0
+pp_data->cap[25]->NotRange.Reserved2 = 0
+pp_data->cap[25]->NotRange.DesignatorIndex = 0
+pp_data->cap[25]->NotRange.Reserved3 = 0
+pp_data->cap[25]->NotRange.DataIndex = 9
+pp_data->cap[25]->NotRange.Reserved4 = 9
+pp_data->cap[25]->Button.LogicalMin = 0
+pp_data->cap[25]->Button.LogicalMax = 0
+pp_data->cap[25]->Units = 0
+pp_data->cap[25]->UnitsExp = 0
+
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0003
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0xFFA0
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/047F_C056_0003_FFA0_expected.rpt_desc b/src/hidapi/windows/test/data/047F_C056_0003_FFA0_expected.rpt_desc
new file mode 100644
index 0000000..ef059c4
--- /dev/null
+++ b/src/hidapi/windows/test/data/047F_C056_0003_FFA0_expected.rpt_desc
@@ -0,0 +1,24 @@
+0x06, 0xA0, 0xFF, 0x09, 0x03, 0xA1, 0x01, 0x85, 0x03, 0x09,
+0x30, 0x15, 0x00, 0x25, 0x01, 0x75, 0x08, 0x95, 0x20, 0x81,
+0x02, 0x85, 0x14, 0x09, 0xB1, 0x09, 0xB2, 0x09, 0xB5, 0x09,
+0xB7, 0x09, 0xB3, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95,
+0x05, 0x81, 0x06, 0x75, 0x03, 0x95, 0x01, 0x81, 0x03, 0x85,
+0x15, 0x09, 0x8C, 0x15, 0x00, 0x27, 0xFF, 0xFF, 0x00, 0x00,
+0x75, 0x10, 0x95, 0x01, 0x81, 0x22, 0x85, 0x1F, 0x09, 0x9C,
+0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0x81, 0x06,
+0x75, 0x07, 0x95, 0x01, 0x81, 0x03, 0x85, 0x03, 0x09, 0x30,
+0x15, 0x00, 0x25, 0x01, 0x75, 0x08, 0x95, 0x20, 0x91, 0x02,
+0x85, 0x19, 0x09, 0x8D, 0x09, 0x8F, 0x09, 0x9E, 0x09, 0xDC,
+0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x04, 0x91, 0x22,
+0x09, 0xD2, 0x09, 0xD9, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01,
+0x95, 0x02, 0x91, 0x06, 0x75, 0x02, 0x95, 0x01, 0x91, 0x03,
+0x85, 0x1A, 0x09, 0xB5, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01,
+0x95, 0x01, 0x91, 0x22, 0x75, 0x07, 0x95, 0x01, 0x91, 0x03,
+0x85, 0x1B, 0x09, 0xCF, 0x09, 0xB5, 0x15, 0x00, 0x25, 0x01,
+0x75, 0x01, 0x95, 0x02, 0xB1, 0x22, 0x09, 0xDE, 0x15, 0x00,
+0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0xB1, 0x23, 0x09, 0xD8,
+0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0xB1, 0x22,
+0x75, 0x04, 0x95, 0x01, 0xB1, 0x03, 0x09, 0x09, 0x09, 0x17,
+0x09, 0x18, 0x09, 0x1E, 0x09, 0x20, 0x09, 0x2A, 0x15, 0x00,
+0x25, 0x01, 0x75, 0x01, 0x95, 0x06, 0xB1, 0x22, 0x75, 0x02,
+0x95, 0x01, 0xB1, 0x03, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/047F_C056_0003_FFA0_real.rpt_desc b/src/hidapi/windows/test/data/047F_C056_0003_FFA0_real.rpt_desc
new file mode 100644
index 0000000..7532fd1
--- /dev/null
+++ b/src/hidapi/windows/test/data/047F_C056_0003_FFA0_real.rpt_desc
@@ -0,0 +1,113 @@
+macOS USB Prober about 0x047F/0xC056 "Plantronics Blackwire 3220 Series"
+06 A0 FF 09 03 A1 01 85 03 09 30 75
+08 95 20 91 02 85 03 09 30 75 08 95 20 81 02 85
+14 09 B1 09 B2 09 B5 09 B7 09 B3 15 00 25 01 75
+01 95 05 81 06 95 03 81 01 85 15 09 8C 15 00 27
+FF FF 00 00 75 10 95 01 81 22 85 19 09 8D 09 8F
+09 9E 09 DC 15 00 25 01 75 01 95 04 91 22 09 D2
+09 D9 15 00 25 01 75 01 95 02 91 06 95 02 91 01
+85 1A 09 B5 15 00 25 01 75 01 95 01 91 22 95 07
+91 01 85 1B 09 CF 09 B5 75 01 95 02 B1 22 09 DE
+75 01 95 01 B1 23 09 D8 95 01 B1 22 95 04 B1 01
+09 09 09 17 09 18 09 1E 09 20 09 2A 75 01 95 06
+B1 22 95 02 B1 01 85 1F 09 9C 75 01 95 01 81 06
+95 07 81 01 C0
+
+Parser output:
+0x06, 0xA0, 0xFF, // Usage Page (Vendor Defined 0xFFA0)
+0x09, 0x03, // Usage (0x03)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x03, // Report ID (3)
+0x09, 0x30, // Usage (0x30)
+0x75, 0x08, // Report Size (8)
+0x95, 0x20, // Report Count (32)
+0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x85, 0x03, // Report ID (3)
+0x09, 0x30, // Usage (0x30)
+0x75, 0x08, // Report Size (8)
+0x95, 0x20, // Report Count (32)
+0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x85, 0x14, // Report ID (20)
+0x09, 0xB1, // Usage (0xB1)
+0x09, 0xB2, // Usage (0xB2)
+0x09, 0xB5, // Usage (0xB5)
+0x09, 0xB7, // Usage (0xB7)
+0x09, 0xB3, // Usage (0xB3)
+0x15, 0x00, // Logical Minimum (0)
+0x25, 0x01, // Logical Maximum (1)
+0x75, 0x01, // Report Size (1)
+0x95, 0x05, // Report Count (5)
+0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+0x95, 0x03, // Report Count (3)
+0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x85, 0x15, // Report ID (21)
+0x09, 0x8C, // Usage (0x8C)
+0x15, 0x00, // Logical Minimum (0)
+0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534)
+0x75, 0x10, // Report Size (16)
+0x95, 0x01, // Report Count (1)
+0x81, 0x22, // Input (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position)
+0x85, 0x19, // Report ID (25)
+0x09, 0x8D, // Usage (0x8D)
+0x09, 0x8F, // Usage (0x8F)
+0x09, 0x9E, // Usage (0x9E)
+0x09, 0xDC, // Usage (0xDC)
+0x15, 0x00, // Logical Minimum (0)
+0x25, 0x01, // Logical Maximum (1)
+0x75, 0x01, // Report Size (1)
+0x95, 0x04, // Report Count (4)
+0x91, 0x22, // Output (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
+0x09, 0xD2, // Usage (0xD2)
+0x09, 0xD9, // Usage (0xD9)
+0x15, 0x00, // Logical Minimum (0)
+0x25, 0x01, // Logical Maximum (1)
+0x75, 0x01, // Report Size (1)
+0x95, 0x02, // Report Count (2)
+0x91, 0x06, // Output (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x95, 0x02, // Report Count (2)
+0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x85, 0x1A, // Report ID (26)
+0x09, 0xB5, // Usage (0xB5)
+0x15, 0x00, // Logical Minimum (0)
+0x25, 0x01, // Logical Maximum (1)
+0x75, 0x01, // Report Size (1)
+0x95, 0x01, // Report Count (1)
+0x91, 0x22, // Output (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
+0x95, 0x07, // Report Count (7)
+0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x85, 0x1B, // Report ID (27)
+0x09, 0xCF, // Usage (0xCF)
+0x09, 0xB5, // Usage (0xB5)
+0x75, 0x01, // Report Size (1)
+0x95, 0x02, // Report Count (2)
+0xB1, 0x22, // Feature (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
+0x09, 0xDE, // Usage (0xDE)
+0x75, 0x01, // Report Size (1)
+0x95, 0x01, // Report Count (1)
+0xB1, 0x23, // Feature (Const,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
+0x09, 0xD8, // Usage (0xD8)
+0x95, 0x01, // Report Count (1)
+0xB1, 0x22, // Feature (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
+0x95, 0x04, // Report Count (4)
+0xB1, 0x01, // Feature (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x09, 0x09, // Usage (0x09)
+0x09, 0x17, // Usage (0x17)
+0x09, 0x18, // Usage (0x18)
+0x09, 0x1E, // Usage (0x1E)
+0x09, 0x20, // Usage (0x20)
+0x09, 0x2A, // Usage (0x2A)
+0x75, 0x01, // Report Size (1)
+0x95, 0x06, // Report Count (6)
+0xB1, 0x22, // Feature (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
+0x95, 0x02, // Report Count (2)
+0xB1, 0x01, // Feature (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x85, 0x1F, // Report ID (31)
+0x09, 0x9C, // Usage (0x9C)
+0x75, 0x01, // Report Size (1)
+0x95, 0x01, // Report Count (1)
+0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+0x95, 0x07, // Report Count (7)
+0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0xC0, // End Collection
+
+// 193 bytes
diff --git a/src/hidapi/windows/test/data/047F_C056_0005_000B.pp_data b/src/hidapi/windows/test/data/047F_C056_0005_000B.pp_data
new file mode 100644
index 0000000..583c317
--- /dev/null
+++ b/src/hidapi/windows/test/data/047F_C056_0005_000B.pp_data
@@ -0,0 +1,461 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x047F
+dev->product_id = 0xC056
+dev->manufacturer_string = "Plantronics"
+dev->product_string = "Plantronics Blackwire 3220 Series"
+dev->release_number = 0x0210
+dev->interface_number = 3
+dev->usage = 0x0005
+dev->usage_page = 0x000B
+dev->path = "\\?\hid#vid_047f&pid_c056&mi_03&col02#f&39e6f119&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0005
+pp_data->UsagePage = 0x000B
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 3
+pp_data->caps_info[0]->NumberOfCaps = 3
+pp_data->caps_info[0]->ReportByteLength = 2
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 3
+pp_data->caps_info[1]->LastCap = 9
+pp_data->caps_info[1]->NumberOfCaps = 6
+pp_data->caps_info[1]->ReportByteLength = 2
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 9
+pp_data->caps_info[2]->LastCap = 9
+pp_data->caps_info[2]->NumberOfCaps = 0
+pp_data->caps_info[2]->ReportByteLength = 0
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x03A8
+pp_data->NumberLinkCollectionNodes = 1
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0x000B
+pp_data->cap[0]->ReportID = 0x08
+pp_data->cap[0]->BitPosition = 0
+pp_data->cap[0]->BitSize = 1
+pp_data->cap[0]->ReportCount = 1
+pp_data->cap[0]->BytePosition = 0x0001
+pp_data->cap[0]->BitCount = 1
+pp_data->cap[0]->BitField = 0x06
+pp_data->cap[0]->NextBytePosition = 0x0002
+pp_data->cap[0]->LinkCollection = 0x0000
+pp_data->cap[0]->LinkUsagePage = 0x000B
+pp_data->cap[0]->LinkUsage = 0x0005
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 1
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 0
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x002F
+pp_data->cap[0]->NotRange.Reserved1 = 0x002F
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->Button.LogicalMin = 0
+pp_data->cap[0]->Button.LogicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0x000B
+pp_data->cap[1]->ReportID = 0x08
+pp_data->cap[1]->BitPosition = 2
+pp_data->cap[1]->BitSize = 1
+pp_data->cap[1]->ReportCount = 1
+pp_data->cap[1]->BytePosition = 0x0001
+pp_data->cap[1]->BitCount = 1
+pp_data->cap[1]->BitField = 0x22
+pp_data->cap[1]->NextBytePosition = 0x0002
+pp_data->cap[1]->LinkCollection = 0x0000
+pp_data->cap[1]->LinkUsagePage = 0x000B
+pp_data->cap[1]->LinkUsage = 0x0005
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 1
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0021
+pp_data->cap[1]->NotRange.Reserved1 = 0x0021
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 1
+pp_data->cap[1]->NotRange.Reserved4 = 1
+pp_data->cap[1]->Button.LogicalMin = 0
+pp_data->cap[1]->Button.LogicalMax = 0
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+pp_data->cap[2]->UsagePage = 0x000B
+pp_data->cap[2]->ReportID = 0x08
+pp_data->cap[2]->BitPosition = 1
+pp_data->cap[2]->BitSize = 1
+pp_data->cap[2]->ReportCount = 1
+pp_data->cap[2]->BytePosition = 0x0001
+pp_data->cap[2]->BitCount = 1
+pp_data->cap[2]->BitField = 0x22
+pp_data->cap[2]->NextBytePosition = 0x0002
+pp_data->cap[2]->LinkCollection = 0x0000
+pp_data->cap[2]->LinkUsagePage = 0x000B
+pp_data->cap[2]->LinkUsage = 0x0005
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 1
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 1
+pp_data->cap[2]->IsRange = 0
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->NotRange.Usage = 0x0020
+pp_data->cap[2]->NotRange.Reserved1 = 0x0020
+pp_data->cap[2]->NotRange.StringIndex = 0
+pp_data->cap[2]->NotRange.Reserved2 = 0
+pp_data->cap[2]->NotRange.DesignatorIndex = 0
+pp_data->cap[2]->NotRange.Reserved3 = 0
+pp_data->cap[2]->NotRange.DataIndex = 2
+pp_data->cap[2]->NotRange.Reserved4 = 2
+pp_data->cap[2]->Button.LogicalMin = 0
+pp_data->cap[2]->Button.LogicalMax = 0
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[3]->UsagePage = 0x0008
+pp_data->cap[3]->ReportID = 0x09
+pp_data->cap[3]->BitPosition = 0
+pp_data->cap[3]->BitSize = 1
+pp_data->cap[3]->ReportCount = 1
+pp_data->cap[3]->BytePosition = 0x0001
+pp_data->cap[3]->BitCount = 1
+pp_data->cap[3]->BitField = 0x22
+pp_data->cap[3]->NextBytePosition = 0x0002
+pp_data->cap[3]->LinkCollection = 0x0000
+pp_data->cap[3]->LinkUsagePage = 0x000B
+pp_data->cap[3]->LinkUsage = 0x0005
+pp_data->cap[3]->IsMultipleItemsForArray = 0
+pp_data->cap[3]->IsButtonCap = 1
+pp_data->cap[3]->IsPadding = 0
+pp_data->cap[3]->IsAbsolute = 1
+pp_data->cap[3]->IsRange = 0
+pp_data->cap[3]->IsAlias = 0
+pp_data->cap[3]->IsStringRange = 0
+pp_data->cap[3]->IsDesignatorRange = 0
+pp_data->cap[3]->Reserved1 = 0x000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[3]->NotRange.Usage = 0x0009
+pp_data->cap[3]->NotRange.Reserved1 = 0x0009
+pp_data->cap[3]->NotRange.StringIndex = 0
+pp_data->cap[3]->NotRange.Reserved2 = 0
+pp_data->cap[3]->NotRange.DesignatorIndex = 0
+pp_data->cap[3]->NotRange.Reserved3 = 0
+pp_data->cap[3]->NotRange.DataIndex = 0
+pp_data->cap[3]->NotRange.Reserved4 = 0
+pp_data->cap[3]->Button.LogicalMin = 0
+pp_data->cap[3]->Button.LogicalMax = 0
+pp_data->cap[3]->Units = 0
+pp_data->cap[3]->UnitsExp = 0
+
+pp_data->cap[4]->UsagePage = 0x0008
+pp_data->cap[4]->ReportID = 0x17
+pp_data->cap[4]->BitPosition = 0
+pp_data->cap[4]->BitSize = 1
+pp_data->cap[4]->ReportCount = 1
+pp_data->cap[4]->BytePosition = 0x0001
+pp_data->cap[4]->BitCount = 1
+pp_data->cap[4]->BitField = 0x22
+pp_data->cap[4]->NextBytePosition = 0x0002
+pp_data->cap[4]->LinkCollection = 0x0000
+pp_data->cap[4]->LinkUsagePage = 0x000B
+pp_data->cap[4]->LinkUsage = 0x0005
+pp_data->cap[4]->IsMultipleItemsForArray = 0
+pp_data->cap[4]->IsButtonCap = 1
+pp_data->cap[4]->IsPadding = 0
+pp_data->cap[4]->IsAbsolute = 1
+pp_data->cap[4]->IsRange = 0
+pp_data->cap[4]->IsAlias = 0
+pp_data->cap[4]->IsStringRange = 0
+pp_data->cap[4]->IsDesignatorRange = 0
+pp_data->cap[4]->Reserved1 = 0x000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[4]->NotRange.Usage = 0x0017
+pp_data->cap[4]->NotRange.Reserved1 = 0x0017
+pp_data->cap[4]->NotRange.StringIndex = 0
+pp_data->cap[4]->NotRange.Reserved2 = 0
+pp_data->cap[4]->NotRange.DesignatorIndex = 0
+pp_data->cap[4]->NotRange.Reserved3 = 0
+pp_data->cap[4]->NotRange.DataIndex = 1
+pp_data->cap[4]->NotRange.Reserved4 = 1
+pp_data->cap[4]->Button.LogicalMin = 0
+pp_data->cap[4]->Button.LogicalMax = 0
+pp_data->cap[4]->Units = 0
+pp_data->cap[4]->UnitsExp = 0
+
+pp_data->cap[5]->UsagePage = 0x0008
+pp_data->cap[5]->ReportID = 0x18
+pp_data->cap[5]->BitPosition = 0
+pp_data->cap[5]->BitSize = 1
+pp_data->cap[5]->ReportCount = 1
+pp_data->cap[5]->BytePosition = 0x0001
+pp_data->cap[5]->BitCount = 1
+pp_data->cap[5]->BitField = 0x22
+pp_data->cap[5]->NextBytePosition = 0x0002
+pp_data->cap[5]->LinkCollection = 0x0000
+pp_data->cap[5]->LinkUsagePage = 0x000B
+pp_data->cap[5]->LinkUsage = 0x0005
+pp_data->cap[5]->IsMultipleItemsForArray = 0
+pp_data->cap[5]->IsButtonCap = 1
+pp_data->cap[5]->IsPadding = 0
+pp_data->cap[5]->IsAbsolute = 1
+pp_data->cap[5]->IsRange = 0
+pp_data->cap[5]->IsAlias = 0
+pp_data->cap[5]->IsStringRange = 0
+pp_data->cap[5]->IsDesignatorRange = 0
+pp_data->cap[5]->Reserved1 = 0x000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[5]->NotRange.Usage = 0x0018
+pp_data->cap[5]->NotRange.Reserved1 = 0x0018
+pp_data->cap[5]->NotRange.StringIndex = 0
+pp_data->cap[5]->NotRange.Reserved2 = 0
+pp_data->cap[5]->NotRange.DesignatorIndex = 0
+pp_data->cap[5]->NotRange.Reserved3 = 0
+pp_data->cap[5]->NotRange.DataIndex = 2
+pp_data->cap[5]->NotRange.Reserved4 = 2
+pp_data->cap[5]->Button.LogicalMin = 0
+pp_data->cap[5]->Button.LogicalMax = 0
+pp_data->cap[5]->Units = 0
+pp_data->cap[5]->UnitsExp = 0
+
+pp_data->cap[6]->UsagePage = 0x0008
+pp_data->cap[6]->ReportID = 0x1E
+pp_data->cap[6]->BitPosition = 0
+pp_data->cap[6]->BitSize = 1
+pp_data->cap[6]->ReportCount = 1
+pp_data->cap[6]->BytePosition = 0x0001
+pp_data->cap[6]->BitCount = 1
+pp_data->cap[6]->BitField = 0x22
+pp_data->cap[6]->NextBytePosition = 0x0002
+pp_data->cap[6]->LinkCollection = 0x0000
+pp_data->cap[6]->LinkUsagePage = 0x000B
+pp_data->cap[6]->LinkUsage = 0x0005
+pp_data->cap[6]->IsMultipleItemsForArray = 0
+pp_data->cap[6]->IsButtonCap = 1
+pp_data->cap[6]->IsPadding = 0
+pp_data->cap[6]->IsAbsolute = 1
+pp_data->cap[6]->IsRange = 0
+pp_data->cap[6]->IsAlias = 0
+pp_data->cap[6]->IsStringRange = 0
+pp_data->cap[6]->IsDesignatorRange = 0
+pp_data->cap[6]->Reserved1 = 0x000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[6]->NotRange.Usage = 0x001E
+pp_data->cap[6]->NotRange.Reserved1 = 0x001E
+pp_data->cap[6]->NotRange.StringIndex = 0
+pp_data->cap[6]->NotRange.Reserved2 = 0
+pp_data->cap[6]->NotRange.DesignatorIndex = 0
+pp_data->cap[6]->NotRange.Reserved3 = 0
+pp_data->cap[6]->NotRange.DataIndex = 3
+pp_data->cap[6]->NotRange.Reserved4 = 3
+pp_data->cap[6]->Button.LogicalMin = 0
+pp_data->cap[6]->Button.LogicalMax = 0
+pp_data->cap[6]->Units = 0
+pp_data->cap[6]->UnitsExp = 0
+
+pp_data->cap[7]->UsagePage = 0x0008
+pp_data->cap[7]->ReportID = 0x20
+pp_data->cap[7]->BitPosition = 0
+pp_data->cap[7]->BitSize = 1
+pp_data->cap[7]->ReportCount = 1
+pp_data->cap[7]->BytePosition = 0x0001
+pp_data->cap[7]->BitCount = 1
+pp_data->cap[7]->BitField = 0x22
+pp_data->cap[7]->NextBytePosition = 0x0002
+pp_data->cap[7]->LinkCollection = 0x0000
+pp_data->cap[7]->LinkUsagePage = 0x000B
+pp_data->cap[7]->LinkUsage = 0x0005
+pp_data->cap[7]->IsMultipleItemsForArray = 0
+pp_data->cap[7]->IsButtonCap = 1
+pp_data->cap[7]->IsPadding = 0
+pp_data->cap[7]->IsAbsolute = 1
+pp_data->cap[7]->IsRange = 0
+pp_data->cap[7]->IsAlias = 0
+pp_data->cap[7]->IsStringRange = 0
+pp_data->cap[7]->IsDesignatorRange = 0
+pp_data->cap[7]->Reserved1 = 0x000
+pp_data->cap[7]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[7]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[7]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[7]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[7]->NotRange.Usage = 0x0020
+pp_data->cap[7]->NotRange.Reserved1 = 0x0020
+pp_data->cap[7]->NotRange.StringIndex = 0
+pp_data->cap[7]->NotRange.Reserved2 = 0
+pp_data->cap[7]->NotRange.DesignatorIndex = 0
+pp_data->cap[7]->NotRange.Reserved3 = 0
+pp_data->cap[7]->NotRange.DataIndex = 4
+pp_data->cap[7]->NotRange.Reserved4 = 4
+pp_data->cap[7]->Button.LogicalMin = 0
+pp_data->cap[7]->Button.LogicalMax = 0
+pp_data->cap[7]->Units = 0
+pp_data->cap[7]->UnitsExp = 0
+
+pp_data->cap[8]->UsagePage = 0x0008
+pp_data->cap[8]->ReportID = 0x2A
+pp_data->cap[8]->BitPosition = 0
+pp_data->cap[8]->BitSize = 1
+pp_data->cap[8]->ReportCount = 1
+pp_data->cap[8]->BytePosition = 0x0001
+pp_data->cap[8]->BitCount = 1
+pp_data->cap[8]->BitField = 0x22
+pp_data->cap[8]->NextBytePosition = 0x0002
+pp_data->cap[8]->LinkCollection = 0x0000
+pp_data->cap[8]->LinkUsagePage = 0x000B
+pp_data->cap[8]->LinkUsage = 0x0005
+pp_data->cap[8]->IsMultipleItemsForArray = 0
+pp_data->cap[8]->IsButtonCap = 1
+pp_data->cap[8]->IsPadding = 0
+pp_data->cap[8]->IsAbsolute = 1
+pp_data->cap[8]->IsRange = 0
+pp_data->cap[8]->IsAlias = 0
+pp_data->cap[8]->IsStringRange = 0
+pp_data->cap[8]->IsDesignatorRange = 0
+pp_data->cap[8]->Reserved1 = 0x000
+pp_data->cap[8]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[8]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[8]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[8]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[8]->NotRange.Usage = 0x002A
+pp_data->cap[8]->NotRange.Reserved1 = 0x002A
+pp_data->cap[8]->NotRange.StringIndex = 0
+pp_data->cap[8]->NotRange.Reserved2 = 0
+pp_data->cap[8]->NotRange.DesignatorIndex = 0
+pp_data->cap[8]->NotRange.Reserved3 = 0
+pp_data->cap[8]->NotRange.DataIndex = 5
+pp_data->cap[8]->NotRange.Reserved4 = 5
+pp_data->cap[8]->Button.LogicalMin = 0
+pp_data->cap[8]->Button.LogicalMax = 0
+pp_data->cap[8]->Units = 0
+pp_data->cap[8]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0005
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0x000B
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 0
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/047F_C056_0005_000B_expected.rpt_desc b/src/hidapi/windows/test/data/047F_C056_0005_000B_expected.rpt_desc
new file mode 100644
index 0000000..40962ac
--- /dev/null
+++ b/src/hidapi/windows/test/data/047F_C056_0005_000B_expected.rpt_desc
@@ -0,0 +1,17 @@
+0x05, 0x0B, 0x09, 0x05, 0xA1, 0x01, 0x85, 0x08, 0x09, 0x2F,
+0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0x81, 0x06,
+0x09, 0x20, 0x09, 0x21, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01,
+0x95, 0x02, 0x81, 0x22, 0x75, 0x05, 0x95, 0x01, 0x81, 0x03,
+0x85, 0x09, 0x05, 0x08, 0x09, 0x09, 0x15, 0x00, 0x25, 0x01,
+0x75, 0x01, 0x95, 0x01, 0x91, 0x22, 0x75, 0x07, 0x95, 0x01,
+0x91, 0x03, 0x85, 0x17, 0x09, 0x17, 0x15, 0x00, 0x25, 0x01,
+0x75, 0x01, 0x95, 0x01, 0x91, 0x22, 0x75, 0x07, 0x95, 0x01,
+0x91, 0x03, 0x85, 0x18, 0x09, 0x18, 0x15, 0x00, 0x25, 0x01,
+0x75, 0x01, 0x95, 0x01, 0x91, 0x22, 0x75, 0x07, 0x95, 0x01,
+0x91, 0x03, 0x85, 0x1E, 0x09, 0x1E, 0x15, 0x00, 0x25, 0x01,
+0x75, 0x01, 0x95, 0x01, 0x91, 0x22, 0x75, 0x07, 0x95, 0x01,
+0x91, 0x03, 0x85, 0x20, 0x09, 0x20, 0x15, 0x00, 0x25, 0x01,
+0x75, 0x01, 0x95, 0x01, 0x91, 0x22, 0x75, 0x07, 0x95, 0x01,
+0x91, 0x03, 0x85, 0x2A, 0x09, 0x2A, 0x15, 0x00, 0x25, 0x01,
+0x75, 0x01, 0x95, 0x01, 0x91, 0x22, 0x75, 0x07, 0x95, 0x01,
+0x91, 0x03, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/047F_C056_0005_000B_real.rpt_desc b/src/hidapi/windows/test/data/047F_C056_0005_000B_real.rpt_desc
new file mode 100644
index 0000000..2fe387e
--- /dev/null
+++ b/src/hidapi/windows/test/data/047F_C056_0005_000B_real.rpt_desc
@@ -0,0 +1,68 @@
+macOS USB Prober about 0x047F/0xC056 "Plantronics Blackwire 3220 Series"
+05 0B 09 05 A1 01 85 08 15
+00 25 01 09 2F 75 01 95 01 81 06 09 20 09 21 75
+01 95 02 81 22 95 05 81 01 05 08 85 09 09 09 95
+01 91 22 95 07 91 01 85 17 09 17 95 01 91 22 95
+07 91 01 85 18 09 18 95 01 91 22 95 07 91 01 85
+1E 09 1E 95 01 91 22 95 07 91 01 85 20 09 20 95
+01 91 22 95 07 91 01 85 2A 09 2A 95 01 91 22 95
+07 91 01 C0
+
+Parser output:
+0x05, 0x0B, // Usage Page (Telephony)
+0x09, 0x05, // Usage (Headset)
+0xA1, 0x01, // Collection (Application)
+0x85, 0x08, // Report ID (8)
+0x15, 0x00, // Logical Minimum (0)
+0x25, 0x01, // Logical Maximum (1)
+0x09, 0x2F, // Usage (Phone Mute)
+0x75, 0x01, // Report Size (1)
+0x95, 0x01, // Report Count (1)
+0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
+0x09, 0x20, // Usage (Hook Switch)
+0x09, 0x21, // Usage (Flash)
+0x75, 0x01, // Report Size (1)
+0x95, 0x02, // Report Count (2)
+0x81, 0x22, // Input (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position)
+0x95, 0x05, // Report Count (5)
+0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+0x05, 0x08, // Usage Page (LEDs)
+0x85, 0x09, // Report ID (9)
+0x09, 0x09, // Usage (Mute)
+0x95, 0x01, // Report Count (1)
+0x91, 0x22, // Output (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
+0x95, 0x07, // Report Count (7)
+0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x85, 0x17, // Report ID (23)
+0x09, 0x17, // Usage (Off-Hook)
+0x95, 0x01, // Report Count (1)
+0x91, 0x22, // Output (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
+0x95, 0x07, // Report Count (7)
+0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x85, 0x18, // Report ID (24)
+0x09, 0x18, // Usage (Ring)
+0x95, 0x01, // Report Count (1)
+0x91, 0x22, // Output (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
+0x95, 0x07, // Report Count (7)
+0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x85, 0x1E, // Report ID (30)
+0x09, 0x1E, // Usage (Speaker)
+0x95, 0x01, // Report Count (1)
+0x91, 0x22, // Output (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
+0x95, 0x07, // Report Count (7)
+0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x85, 0x20, // Report ID (32)
+0x09, 0x20, // Usage (Hold)
+0x95, 0x01, // Report Count (1)
+0x91, 0x22, // Output (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
+0x95, 0x07, // Report Count (7)
+0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0x85, 0x2A, // Report ID (42)
+0x09, 0x2A, // Usage (On-Line)
+0x95, 0x01, // Report Count (1)
+0x91, 0x22, // Output (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile)
+0x95, 0x07, // Report Count (7)
+0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+0xC0, // End Collection
+
+// 109 bytes
diff --git a/src/hidapi/windows/test/data/17CC_1130_0000_FF01.pp_data b/src/hidapi/windows/test/data/17CC_1130_0000_FF01.pp_data
new file mode 100644
index 0000000..acab8a6
--- /dev/null
+++ b/src/hidapi/windows/test/data/17CC_1130_0000_FF01.pp_data
@@ -0,0 +1,11508 @@
+# HIDAPI device info struct:
+dev->vendor_id = 0x17CC
+dev->product_id = 0x1130
+dev->manufacturer_string = "Native Instruments"
+dev->product_string = "Traktor Kontrol Z2"
+dev->release_number = 0x0033
+dev->interface_number = 4
+dev->usage = 0x0000
+dev->usage_page = 0xFF01
+dev->path = "\\?\hid#vid_17cc&pid_1130&mi_04#9&11d406cd&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
+
+# Preparsed Data struct:
+pp_data->MagicKey = 0x48696450204B4452
+pp_data->Usage = 0x0000
+pp_data->UsagePage = 0xFF01
+pp_data->Reserved = 0x00000000
+# Input caps_info struct:
+pp_data->caps_info[0]->FirstCap = 0
+pp_data->caps_info[0]->LastCap = 86
+pp_data->caps_info[0]->NumberOfCaps = 87
+pp_data->caps_info[0]->ReportByteLength = 53
+# Output caps_info struct:
+pp_data->caps_info[1]->FirstCap = 87
+pp_data->caps_info[1]->LastCap = 221
+pp_data->caps_info[1]->NumberOfCaps = 134
+pp_data->caps_info[1]->ReportByteLength = 95
+# Feature caps_info struct:
+pp_data->caps_info[2]->FirstCap = 221
+pp_data->caps_info[2]->LastCap = 232
+pp_data->caps_info[2]->NumberOfCaps = 11
+pp_data->caps_info[2]->ReportByteLength = 33
+# LinkCollectionArray Offset & Size:
+pp_data->FirstByteOfLinkCollectionArray = 0x5E40
+pp_data->NumberLinkCollectionNodes = 16
+# Input hid_pp_cap struct:
+pp_data->cap[0]->UsagePage = 0xFF01
+pp_data->cap[0]->ReportID = 0x01
+pp_data->cap[0]->BitPosition = 4
+pp_data->cap[0]->BitSize = 4
+pp_data->cap[0]->ReportCount = 1
+pp_data->cap[0]->BytePosition = 0x0002
+pp_data->cap[0]->BitCount = 4
+pp_data->cap[0]->BitField = 0x02
+pp_data->cap[0]->NextBytePosition = 0x0003
+pp_data->cap[0]->LinkCollection = 0x0001
+pp_data->cap[0]->LinkUsagePage = 0xFF01
+pp_data->cap[0]->LinkUsage = 0x0001
+pp_data->cap[0]->IsMultipleItemsForArray = 0
+pp_data->cap[0]->IsButtonCap = 0
+pp_data->cap[0]->IsPadding = 0
+pp_data->cap[0]->IsAbsolute = 1
+pp_data->cap[0]->IsRange = 0
+pp_data->cap[0]->IsAlias = 0
+pp_data->cap[0]->IsStringRange = 0
+pp_data->cap[0]->IsDesignatorRange = 0
+pp_data->cap[0]->Reserved1 = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[0]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[0]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[0]->NotRange.Usage = 0x0003
+pp_data->cap[0]->NotRange.Reserved1 = 0x0003
+pp_data->cap[0]->NotRange.StringIndex = 0
+pp_data->cap[0]->NotRange.Reserved2 = 0
+pp_data->cap[0]->NotRange.DesignatorIndex = 0
+pp_data->cap[0]->NotRange.Reserved3 = 0
+pp_data->cap[0]->NotRange.DataIndex = 0
+pp_data->cap[0]->NotRange.Reserved4 = 0
+pp_data->cap[0]->NotButton.HasNull = 0
+pp_data->cap[0]->NotButton.Reserved4 = 0x000000
+pp_data->cap[0]->NotButton.LogicalMin = 0
+pp_data->cap[0]->NotButton.LogicalMax = 15
+pp_data->cap[0]->NotButton.PhysicalMin = 0
+pp_data->cap[0]->NotButton.PhysicalMax = 0
+pp_data->cap[0]->Units = 0
+pp_data->cap[0]->UnitsExp = 0
+
+pp_data->cap[1]->UsagePage = 0xFF01
+pp_data->cap[1]->ReportID = 0x01
+pp_data->cap[1]->BitPosition = 0
+pp_data->cap[1]->BitSize = 4
+pp_data->cap[1]->ReportCount = 1
+pp_data->cap[1]->BytePosition = 0x0002
+pp_data->cap[1]->BitCount = 4
+pp_data->cap[1]->BitField = 0x02
+pp_data->cap[1]->NextBytePosition = 0x0003
+pp_data->cap[1]->LinkCollection = 0x0001
+pp_data->cap[1]->LinkUsagePage = 0xFF01
+pp_data->cap[1]->LinkUsage = 0x0001
+pp_data->cap[1]->IsMultipleItemsForArray = 0
+pp_data->cap[1]->IsButtonCap = 0
+pp_data->cap[1]->IsPadding = 0
+pp_data->cap[1]->IsAbsolute = 1
+pp_data->cap[1]->IsRange = 0
+pp_data->cap[1]->IsAlias = 0
+pp_data->cap[1]->IsStringRange = 0
+pp_data->cap[1]->IsDesignatorRange = 0
+pp_data->cap[1]->Reserved1 = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[1]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[1]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[1]->NotRange.Usage = 0x0003
+pp_data->cap[1]->NotRange.Reserved1 = 0x0003
+pp_data->cap[1]->NotRange.StringIndex = 0
+pp_data->cap[1]->NotRange.Reserved2 = 0
+pp_data->cap[1]->NotRange.DesignatorIndex = 0
+pp_data->cap[1]->NotRange.Reserved3 = 0
+pp_data->cap[1]->NotRange.DataIndex = 1
+pp_data->cap[1]->NotRange.Reserved4 = 1
+pp_data->cap[1]->NotButton.HasNull = 0
+pp_data->cap[1]->NotButton.Reserved4 = 0x000000
+pp_data->cap[1]->NotButton.LogicalMin = 0
+pp_data->cap[1]->NotButton.LogicalMax = 15
+pp_data->cap[1]->NotButton.PhysicalMin = 0
+pp_data->cap[1]->NotButton.PhysicalMax = 0
+pp_data->cap[1]->Units = 0
+pp_data->cap[1]->UnitsExp = 0
+
+pp_data->cap[2]->UsagePage = 0xFF01
+pp_data->cap[2]->ReportID = 0x01
+pp_data->cap[2]->BitPosition = 4
+pp_data->cap[2]->BitSize = 4
+pp_data->cap[2]->ReportCount = 1
+pp_data->cap[2]->BytePosition = 0x0001
+pp_data->cap[2]->BitCount = 4
+pp_data->cap[2]->BitField = 0x02
+pp_data->cap[2]->NextBytePosition = 0x0002
+pp_data->cap[2]->LinkCollection = 0x0001
+pp_data->cap[2]->LinkUsagePage = 0xFF01
+pp_data->cap[2]->LinkUsage = 0x0001
+pp_data->cap[2]->IsMultipleItemsForArray = 0
+pp_data->cap[2]->IsButtonCap = 0
+pp_data->cap[2]->IsPadding = 0
+pp_data->cap[2]->IsAbsolute = 1
+pp_data->cap[2]->IsRange = 0
+pp_data->cap[2]->IsAlias = 0
+pp_data->cap[2]->IsStringRange = 0
+pp_data->cap[2]->IsDesignatorRange = 0
+pp_data->cap[2]->Reserved1 = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[2]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[2]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[2]->NotRange.Usage = 0x0003
+pp_data->cap[2]->NotRange.Reserved1 = 0x0003
+pp_data->cap[2]->NotRange.StringIndex = 0
+pp_data->cap[2]->NotRange.Reserved2 = 0
+pp_data->cap[2]->NotRange.DesignatorIndex = 0
+pp_data->cap[2]->NotRange.Reserved3 = 0
+pp_data->cap[2]->NotRange.DataIndex = 2
+pp_data->cap[2]->NotRange.Reserved4 = 2
+pp_data->cap[2]->NotButton.HasNull = 0
+pp_data->cap[2]->NotButton.Reserved4 = 0x000000
+pp_data->cap[2]->NotButton.LogicalMin = 0
+pp_data->cap[2]->NotButton.LogicalMax = 15
+pp_data->cap[2]->NotButton.PhysicalMin = 0
+pp_data->cap[2]->NotButton.PhysicalMax = 0
+pp_data->cap[2]->Units = 0
+pp_data->cap[2]->UnitsExp = 0
+
+pp_data->cap[3]->UsagePage = 0xFF01
+pp_data->cap[3]->ReportID = 0x01
+pp_data->cap[3]->BitPosition = 0
+pp_data->cap[3]->BitSize = 4
+pp_data->cap[3]->ReportCount = 1
+pp_data->cap[3]->BytePosition = 0x0001
+pp_data->cap[3]->BitCount = 4
+pp_data->cap[3]->BitField = 0x02
+pp_data->cap[3]->NextBytePosition = 0x0002
+pp_data->cap[3]->LinkCollection = 0x0001
+pp_data->cap[3]->LinkUsagePage = 0xFF01
+pp_data->cap[3]->LinkUsage = 0x0001
+pp_data->cap[3]->IsMultipleItemsForArray = 0
+pp_data->cap[3]->IsButtonCap = 0
+pp_data->cap[3]->IsPadding = 0
+pp_data->cap[3]->IsAbsolute = 1
+pp_data->cap[3]->IsRange = 0
+pp_data->cap[3]->IsAlias = 0
+pp_data->cap[3]->IsStringRange = 0
+pp_data->cap[3]->IsDesignatorRange = 0
+pp_data->cap[3]->Reserved1 = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[3]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[3]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[3]->NotRange.Usage = 0x0003
+pp_data->cap[3]->NotRange.Reserved1 = 0x0003
+pp_data->cap[3]->NotRange.StringIndex = 0
+pp_data->cap[3]->NotRange.Reserved2 = 0
+pp_data->cap[3]->NotRange.DesignatorIndex = 0
+pp_data->cap[3]->NotRange.Reserved3 = 0
+pp_data->cap[3]->NotRange.DataIndex = 3
+pp_data->cap[3]->NotRange.Reserved4 = 3
+pp_data->cap[3]->NotButton.HasNull = 0
+pp_data->cap[3]->NotButton.Reserved4 = 0x000000
+pp_data->cap[3]->NotButton.LogicalMin = 0
+pp_data->cap[3]->NotButton.LogicalMax = 15
+pp_data->cap[3]->NotButton.PhysicalMin = 0
+pp_data->cap[3]->NotButton.PhysicalMax = 0
+pp_data->cap[3]->Units = 0
+pp_data->cap[3]->UnitsExp = 0
+
+pp_data->cap[4]->UsagePage = 0xFF01
+pp_data->cap[4]->ReportID = 0x01
+pp_data->cap[4]->BitPosition = 7
+pp_data->cap[4]->BitSize = 1
+pp_data->cap[4]->ReportCount = 1
+pp_data->cap[4]->BytePosition = 0x0008
+pp_data->cap[4]->BitCount = 1
+pp_data->cap[4]->BitField = 0x02
+pp_data->cap[4]->NextBytePosition = 0x0009
+pp_data->cap[4]->LinkCollection = 0x0001
+pp_data->cap[4]->LinkUsagePage = 0xFF01
+pp_data->cap[4]->LinkUsage = 0x0001
+pp_data->cap[4]->IsMultipleItemsForArray = 0
+pp_data->cap[4]->IsButtonCap = 1
+pp_data->cap[4]->IsPadding = 0
+pp_data->cap[4]->IsAbsolute = 1
+pp_data->cap[4]->IsRange = 0
+pp_data->cap[4]->IsAlias = 0
+pp_data->cap[4]->IsStringRange = 0
+pp_data->cap[4]->IsDesignatorRange = 0
+pp_data->cap[4]->Reserved1 = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[4]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[4]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[4]->NotRange.Usage = 0x0002
+pp_data->cap[4]->NotRange.Reserved1 = 0x0002
+pp_data->cap[4]->NotRange.StringIndex = 0
+pp_data->cap[4]->NotRange.Reserved2 = 0
+pp_data->cap[4]->NotRange.DesignatorIndex = 0
+pp_data->cap[4]->NotRange.Reserved3 = 0
+pp_data->cap[4]->NotRange.DataIndex = 4
+pp_data->cap[4]->NotRange.Reserved4 = 4
+pp_data->cap[4]->Button.LogicalMin = 0
+pp_data->cap[4]->Button.LogicalMax = 0
+pp_data->cap[4]->Units = 0
+pp_data->cap[4]->UnitsExp = 0
+
+pp_data->cap[5]->UsagePage = 0xFF01
+pp_data->cap[5]->ReportID = 0x01
+pp_data->cap[5]->BitPosition = 6
+pp_data->cap[5]->BitSize = 1
+pp_data->cap[5]->ReportCount = 1
+pp_data->cap[5]->BytePosition = 0x0008
+pp_data->cap[5]->BitCount = 1
+pp_data->cap[5]->BitField = 0x02
+pp_data->cap[5]->NextBytePosition = 0x0009
+pp_data->cap[5]->LinkCollection = 0x0001
+pp_data->cap[5]->LinkUsagePage = 0xFF01
+pp_data->cap[5]->LinkUsage = 0x0001
+pp_data->cap[5]->IsMultipleItemsForArray = 0
+pp_data->cap[5]->IsButtonCap = 1
+pp_data->cap[5]->IsPadding = 0
+pp_data->cap[5]->IsAbsolute = 1
+pp_data->cap[5]->IsRange = 0
+pp_data->cap[5]->IsAlias = 0
+pp_data->cap[5]->IsStringRange = 0
+pp_data->cap[5]->IsDesignatorRange = 0
+pp_data->cap[5]->Reserved1 = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[5]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[5]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[5]->NotRange.Usage = 0x0002
+pp_data->cap[5]->NotRange.Reserved1 = 0x0002
+pp_data->cap[5]->NotRange.StringIndex = 0
+pp_data->cap[5]->NotRange.Reserved2 = 0
+pp_data->cap[5]->NotRange.DesignatorIndex = 0
+pp_data->cap[5]->NotRange.Reserved3 = 0
+pp_data->cap[5]->NotRange.DataIndex = 5
+pp_data->cap[5]->NotRange.Reserved4 = 5
+pp_data->cap[5]->Button.LogicalMin = 0
+pp_data->cap[5]->Button.LogicalMax = 0
+pp_data->cap[5]->Units = 0
+pp_data->cap[5]->UnitsExp = 0
+
+pp_data->cap[6]->UsagePage = 0xFF01
+pp_data->cap[6]->ReportID = 0x01
+pp_data->cap[6]->BitPosition = 5
+pp_data->cap[6]->BitSize = 1
+pp_data->cap[6]->ReportCount = 1
+pp_data->cap[6]->BytePosition = 0x0008
+pp_data->cap[6]->BitCount = 1
+pp_data->cap[6]->BitField = 0x02
+pp_data->cap[6]->NextBytePosition = 0x0009
+pp_data->cap[6]->LinkCollection = 0x0001
+pp_data->cap[6]->LinkUsagePage = 0xFF01
+pp_data->cap[6]->LinkUsage = 0x0001
+pp_data->cap[6]->IsMultipleItemsForArray = 0
+pp_data->cap[6]->IsButtonCap = 1
+pp_data->cap[6]->IsPadding = 0
+pp_data->cap[6]->IsAbsolute = 1
+pp_data->cap[6]->IsRange = 0
+pp_data->cap[6]->IsAlias = 0
+pp_data->cap[6]->IsStringRange = 0
+pp_data->cap[6]->IsDesignatorRange = 0
+pp_data->cap[6]->Reserved1 = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[6]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[6]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[6]->NotRange.Usage = 0x0002
+pp_data->cap[6]->NotRange.Reserved1 = 0x0002
+pp_data->cap[6]->NotRange.StringIndex = 0
+pp_data->cap[6]->NotRange.Reserved2 = 0
+pp_data->cap[6]->NotRange.DesignatorIndex = 0
+pp_data->cap[6]->NotRange.Reserved3 = 0
+pp_data->cap[6]->NotRange.DataIndex = 6
+pp_data->cap[6]->NotRange.Reserved4 = 6
+pp_data->cap[6]->Button.LogicalMin = 0
+pp_data->cap[6]->Button.LogicalMax = 0
+pp_data->cap[6]->Units = 0
+pp_data->cap[6]->UnitsExp = 0
+
+pp_data->cap[7]->UsagePage = 0xFF01
+pp_data->cap[7]->ReportID = 0x01
+pp_data->cap[7]->BitPosition = 4
+pp_data->cap[7]->BitSize = 1
+pp_data->cap[7]->ReportCount = 1
+pp_data->cap[7]->BytePosition = 0x0008
+pp_data->cap[7]->BitCount = 1
+pp_data->cap[7]->BitField = 0x02
+pp_data->cap[7]->NextBytePosition = 0x0009
+pp_data->cap[7]->LinkCollection = 0x0001
+pp_data->cap[7]->LinkUsagePage = 0xFF01
+pp_data->cap[7]->LinkUsage = 0x0001
+pp_data->cap[7]->IsMultipleItemsForArray = 0
+pp_data->cap[7]->IsButtonCap = 1
+pp_data->cap[7]->IsPadding = 0
+pp_data->cap[7]->IsAbsolute = 1
+pp_data->cap[7]->IsRange = 0
+pp_data->cap[7]->IsAlias = 0
+pp_data->cap[7]->IsStringRange = 0
+pp_data->cap[7]->IsDesignatorRange = 0
+pp_data->cap[7]->Reserved1 = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[7]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[7]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[7]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[7]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[7]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[7]->NotRange.Usage = 0x0002
+pp_data->cap[7]->NotRange.Reserved1 = 0x0002
+pp_data->cap[7]->NotRange.StringIndex = 0
+pp_data->cap[7]->NotRange.Reserved2 = 0
+pp_data->cap[7]->NotRange.DesignatorIndex = 0
+pp_data->cap[7]->NotRange.Reserved3 = 0
+pp_data->cap[7]->NotRange.DataIndex = 7
+pp_data->cap[7]->NotRange.Reserved4 = 7
+pp_data->cap[7]->Button.LogicalMin = 0
+pp_data->cap[7]->Button.LogicalMax = 0
+pp_data->cap[7]->Units = 0
+pp_data->cap[7]->UnitsExp = 0
+
+pp_data->cap[8]->UsagePage = 0xFF01
+pp_data->cap[8]->ReportID = 0x01
+pp_data->cap[8]->BitPosition = 3
+pp_data->cap[8]->BitSize = 1
+pp_data->cap[8]->ReportCount = 1
+pp_data->cap[8]->BytePosition = 0x0008
+pp_data->cap[8]->BitCount = 1
+pp_data->cap[8]->BitField = 0x02
+pp_data->cap[8]->NextBytePosition = 0x0009
+pp_data->cap[8]->LinkCollection = 0x0001
+pp_data->cap[8]->LinkUsagePage = 0xFF01
+pp_data->cap[8]->LinkUsage = 0x0001
+pp_data->cap[8]->IsMultipleItemsForArray = 0
+pp_data->cap[8]->IsButtonCap = 1
+pp_data->cap[8]->IsPadding = 0
+pp_data->cap[8]->IsAbsolute = 1
+pp_data->cap[8]->IsRange = 0
+pp_data->cap[8]->IsAlias = 0
+pp_data->cap[8]->IsStringRange = 0
+pp_data->cap[8]->IsDesignatorRange = 0
+pp_data->cap[8]->Reserved1 = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[8]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[8]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[8]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[8]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[8]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[8]->NotRange.Usage = 0x0002
+pp_data->cap[8]->NotRange.Reserved1 = 0x0002
+pp_data->cap[8]->NotRange.StringIndex = 0
+pp_data->cap[8]->NotRange.Reserved2 = 0
+pp_data->cap[8]->NotRange.DesignatorIndex = 0
+pp_data->cap[8]->NotRange.Reserved3 = 0
+pp_data->cap[8]->NotRange.DataIndex = 8
+pp_data->cap[8]->NotRange.Reserved4 = 8
+pp_data->cap[8]->Button.LogicalMin = 0
+pp_data->cap[8]->Button.LogicalMax = 0
+pp_data->cap[8]->Units = 0
+pp_data->cap[8]->UnitsExp = 0
+
+pp_data->cap[9]->UsagePage = 0xFF01
+pp_data->cap[9]->ReportID = 0x01
+pp_data->cap[9]->BitPosition = 2
+pp_data->cap[9]->BitSize = 1
+pp_data->cap[9]->ReportCount = 1
+pp_data->cap[9]->BytePosition = 0x0008
+pp_data->cap[9]->BitCount = 1
+pp_data->cap[9]->BitField = 0x02
+pp_data->cap[9]->NextBytePosition = 0x0009
+pp_data->cap[9]->LinkCollection = 0x0001
+pp_data->cap[9]->LinkUsagePage = 0xFF01
+pp_data->cap[9]->LinkUsage = 0x0001
+pp_data->cap[9]->IsMultipleItemsForArray = 0
+pp_data->cap[9]->IsButtonCap = 1
+pp_data->cap[9]->IsPadding = 0
+pp_data->cap[9]->IsAbsolute = 1
+pp_data->cap[9]->IsRange = 0
+pp_data->cap[9]->IsAlias = 0
+pp_data->cap[9]->IsStringRange = 0
+pp_data->cap[9]->IsDesignatorRange = 0
+pp_data->cap[9]->Reserved1 = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[9]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[9]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[9]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[9]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[9]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[9]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[9]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[9]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[9]->NotRange.Usage = 0x0002
+pp_data->cap[9]->NotRange.Reserved1 = 0x0002
+pp_data->cap[9]->NotRange.StringIndex = 0
+pp_data->cap[9]->NotRange.Reserved2 = 0
+pp_data->cap[9]->NotRange.DesignatorIndex = 0
+pp_data->cap[9]->NotRange.Reserved3 = 0
+pp_data->cap[9]->NotRange.DataIndex = 9
+pp_data->cap[9]->NotRange.Reserved4 = 9
+pp_data->cap[9]->Button.LogicalMin = 0
+pp_data->cap[9]->Button.LogicalMax = 0
+pp_data->cap[9]->Units = 0
+pp_data->cap[9]->UnitsExp = 0
+
+pp_data->cap[10]->UsagePage = 0xFF01
+pp_data->cap[10]->ReportID = 0x01
+pp_data->cap[10]->BitPosition = 1
+pp_data->cap[10]->BitSize = 1
+pp_data->cap[10]->ReportCount = 1
+pp_data->cap[10]->BytePosition = 0x0008
+pp_data->cap[10]->BitCount = 1
+pp_data->cap[10]->BitField = 0x02
+pp_data->cap[10]->NextBytePosition = 0x0009
+pp_data->cap[10]->LinkCollection = 0x0001
+pp_data->cap[10]->LinkUsagePage = 0xFF01
+pp_data->cap[10]->LinkUsage = 0x0001
+pp_data->cap[10]->IsMultipleItemsForArray = 0
+pp_data->cap[10]->IsButtonCap = 1
+pp_data->cap[10]->IsPadding = 0
+pp_data->cap[10]->IsAbsolute = 1
+pp_data->cap[10]->IsRange = 0
+pp_data->cap[10]->IsAlias = 0
+pp_data->cap[10]->IsStringRange = 0
+pp_data->cap[10]->IsDesignatorRange = 0
+pp_data->cap[10]->Reserved1 = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[10]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[10]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[10]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[10]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[10]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[10]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[10]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[10]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[10]->NotRange.Usage = 0x0002
+pp_data->cap[10]->NotRange.Reserved1 = 0x0002
+pp_data->cap[10]->NotRange.StringIndex = 0
+pp_data->cap[10]->NotRange.Reserved2 = 0
+pp_data->cap[10]->NotRange.DesignatorIndex = 0
+pp_data->cap[10]->NotRange.Reserved3 = 0
+pp_data->cap[10]->NotRange.DataIndex = 10
+pp_data->cap[10]->NotRange.Reserved4 = 10
+pp_data->cap[10]->Button.LogicalMin = 0
+pp_data->cap[10]->Button.LogicalMax = 0
+pp_data->cap[10]->Units = 0
+pp_data->cap[10]->UnitsExp = 0
+
+pp_data->cap[11]->UsagePage = 0xFF01
+pp_data->cap[11]->ReportID = 0x01
+pp_data->cap[11]->BitPosition = 0
+pp_data->cap[11]->BitSize = 1
+pp_data->cap[11]->ReportCount = 1
+pp_data->cap[11]->BytePosition = 0x0008
+pp_data->cap[11]->BitCount = 1
+pp_data->cap[11]->BitField = 0x02
+pp_data->cap[11]->NextBytePosition = 0x0009
+pp_data->cap[11]->LinkCollection = 0x0001
+pp_data->cap[11]->LinkUsagePage = 0xFF01
+pp_data->cap[11]->LinkUsage = 0x0001
+pp_data->cap[11]->IsMultipleItemsForArray = 0
+pp_data->cap[11]->IsButtonCap = 1
+pp_data->cap[11]->IsPadding = 0
+pp_data->cap[11]->IsAbsolute = 1
+pp_data->cap[11]->IsRange = 0
+pp_data->cap[11]->IsAlias = 0
+pp_data->cap[11]->IsStringRange = 0
+pp_data->cap[11]->IsDesignatorRange = 0
+pp_data->cap[11]->Reserved1 = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[11]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[11]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[11]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[11]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[11]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[11]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[11]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[11]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[11]->NotRange.Usage = 0x0002
+pp_data->cap[11]->NotRange.Reserved1 = 0x0002
+pp_data->cap[11]->NotRange.StringIndex = 0
+pp_data->cap[11]->NotRange.Reserved2 = 0
+pp_data->cap[11]->NotRange.DesignatorIndex = 0
+pp_data->cap[11]->NotRange.Reserved3 = 0
+pp_data->cap[11]->NotRange.DataIndex = 11
+pp_data->cap[11]->NotRange.Reserved4 = 11
+pp_data->cap[11]->Button.LogicalMin = 0
+pp_data->cap[11]->Button.LogicalMax = 0
+pp_data->cap[11]->Units = 0
+pp_data->cap[11]->UnitsExp = 0
+
+pp_data->cap[12]->UsagePage = 0xFF01
+pp_data->cap[12]->ReportID = 0x01
+pp_data->cap[12]->BitPosition = 7
+pp_data->cap[12]->BitSize = 1
+pp_data->cap[12]->ReportCount = 1
+pp_data->cap[12]->BytePosition = 0x0007
+pp_data->cap[12]->BitCount = 1
+pp_data->cap[12]->BitField = 0x02
+pp_data->cap[12]->NextBytePosition = 0x0008
+pp_data->cap[12]->LinkCollection = 0x0001
+pp_data->cap[12]->LinkUsagePage = 0xFF01
+pp_data->cap[12]->LinkUsage = 0x0001
+pp_data->cap[12]->IsMultipleItemsForArray = 0
+pp_data->cap[12]->IsButtonCap = 1
+pp_data->cap[12]->IsPadding = 0
+pp_data->cap[12]->IsAbsolute = 1
+pp_data->cap[12]->IsRange = 0
+pp_data->cap[12]->IsAlias = 0
+pp_data->cap[12]->IsStringRange = 0
+pp_data->cap[12]->IsDesignatorRange = 0
+pp_data->cap[12]->Reserved1 = 0x000000
+pp_data->cap[12]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[12]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[12]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[12]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[12]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[12]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[12]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[12]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[12]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[12]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[12]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[12]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[12]->NotRange.Usage = 0x0002
+pp_data->cap[12]->NotRange.Reserved1 = 0x0002
+pp_data->cap[12]->NotRange.StringIndex = 0
+pp_data->cap[12]->NotRange.Reserved2 = 0
+pp_data->cap[12]->NotRange.DesignatorIndex = 0
+pp_data->cap[12]->NotRange.Reserved3 = 0
+pp_data->cap[12]->NotRange.DataIndex = 12
+pp_data->cap[12]->NotRange.Reserved4 = 12
+pp_data->cap[12]->Button.LogicalMin = 0
+pp_data->cap[12]->Button.LogicalMax = 0
+pp_data->cap[12]->Units = 0
+pp_data->cap[12]->UnitsExp = 0
+
+pp_data->cap[13]->UsagePage = 0xFF01
+pp_data->cap[13]->ReportID = 0x01
+pp_data->cap[13]->BitPosition = 6
+pp_data->cap[13]->BitSize = 1
+pp_data->cap[13]->ReportCount = 1
+pp_data->cap[13]->BytePosition = 0x0007
+pp_data->cap[13]->BitCount = 1
+pp_data->cap[13]->BitField = 0x02
+pp_data->cap[13]->NextBytePosition = 0x0008
+pp_data->cap[13]->LinkCollection = 0x0001
+pp_data->cap[13]->LinkUsagePage = 0xFF01
+pp_data->cap[13]->LinkUsage = 0x0001
+pp_data->cap[13]->IsMultipleItemsForArray = 0
+pp_data->cap[13]->IsButtonCap = 1
+pp_data->cap[13]->IsPadding = 0
+pp_data->cap[13]->IsAbsolute = 1
+pp_data->cap[13]->IsRange = 0
+pp_data->cap[13]->IsAlias = 0
+pp_data->cap[13]->IsStringRange = 0
+pp_data->cap[13]->IsDesignatorRange = 0
+pp_data->cap[13]->Reserved1 = 0x000000
+pp_data->cap[13]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[13]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[13]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[13]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[13]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[13]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[13]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[13]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[13]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[13]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[13]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[13]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[13]->NotRange.Usage = 0x0002
+pp_data->cap[13]->NotRange.Reserved1 = 0x0002
+pp_data->cap[13]->NotRange.StringIndex = 0
+pp_data->cap[13]->NotRange.Reserved2 = 0
+pp_data->cap[13]->NotRange.DesignatorIndex = 0
+pp_data->cap[13]->NotRange.Reserved3 = 0
+pp_data->cap[13]->NotRange.DataIndex = 13
+pp_data->cap[13]->NotRange.Reserved4 = 13
+pp_data->cap[13]->Button.LogicalMin = 0
+pp_data->cap[13]->Button.LogicalMax = 0
+pp_data->cap[13]->Units = 0
+pp_data->cap[13]->UnitsExp = 0
+
+pp_data->cap[14]->UsagePage = 0xFF01
+pp_data->cap[14]->ReportID = 0x01
+pp_data->cap[14]->BitPosition = 5
+pp_data->cap[14]->BitSize = 1
+pp_data->cap[14]->ReportCount = 1
+pp_data->cap[14]->BytePosition = 0x0007
+pp_data->cap[14]->BitCount = 1
+pp_data->cap[14]->BitField = 0x02
+pp_data->cap[14]->NextBytePosition = 0x0008
+pp_data->cap[14]->LinkCollection = 0x0001
+pp_data->cap[14]->LinkUsagePage = 0xFF01
+pp_data->cap[14]->LinkUsage = 0x0001
+pp_data->cap[14]->IsMultipleItemsForArray = 0
+pp_data->cap[14]->IsButtonCap = 1
+pp_data->cap[14]->IsPadding = 0
+pp_data->cap[14]->IsAbsolute = 1
+pp_data->cap[14]->IsRange = 0
+pp_data->cap[14]->IsAlias = 0
+pp_data->cap[14]->IsStringRange = 0
+pp_data->cap[14]->IsDesignatorRange = 0
+pp_data->cap[14]->Reserved1 = 0x000000
+pp_data->cap[14]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[14]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[14]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[14]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[14]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[14]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[14]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[14]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[14]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[14]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[14]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[14]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[14]->NotRange.Usage = 0x0002
+pp_data->cap[14]->NotRange.Reserved1 = 0x0002
+pp_data->cap[14]->NotRange.StringIndex = 0
+pp_data->cap[14]->NotRange.Reserved2 = 0
+pp_data->cap[14]->NotRange.DesignatorIndex = 0
+pp_data->cap[14]->NotRange.Reserved3 = 0
+pp_data->cap[14]->NotRange.DataIndex = 14
+pp_data->cap[14]->NotRange.Reserved4 = 14
+pp_data->cap[14]->Button.LogicalMin = 0
+pp_data->cap[14]->Button.LogicalMax = 0
+pp_data->cap[14]->Units = 0
+pp_data->cap[14]->UnitsExp = 0
+
+pp_data->cap[15]->UsagePage = 0xFF01
+pp_data->cap[15]->ReportID = 0x01
+pp_data->cap[15]->BitPosition = 4
+pp_data->cap[15]->BitSize = 1
+pp_data->cap[15]->ReportCount = 1
+pp_data->cap[15]->BytePosition = 0x0007
+pp_data->cap[15]->BitCount = 1
+pp_data->cap[15]->BitField = 0x02
+pp_data->cap[15]->NextBytePosition = 0x0008
+pp_data->cap[15]->LinkCollection = 0x0001
+pp_data->cap[15]->LinkUsagePage = 0xFF01
+pp_data->cap[15]->LinkUsage = 0x0001
+pp_data->cap[15]->IsMultipleItemsForArray = 0
+pp_data->cap[15]->IsButtonCap = 1
+pp_data->cap[15]->IsPadding = 0
+pp_data->cap[15]->IsAbsolute = 1
+pp_data->cap[15]->IsRange = 0
+pp_data->cap[15]->IsAlias = 0
+pp_data->cap[15]->IsStringRange = 0
+pp_data->cap[15]->IsDesignatorRange = 0
+pp_data->cap[15]->Reserved1 = 0x000000
+pp_data->cap[15]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[15]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[15]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[15]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[15]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[15]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[15]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[15]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[15]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[15]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[15]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[15]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[15]->NotRange.Usage = 0x0002
+pp_data->cap[15]->NotRange.Reserved1 = 0x0002
+pp_data->cap[15]->NotRange.StringIndex = 0
+pp_data->cap[15]->NotRange.Reserved2 = 0
+pp_data->cap[15]->NotRange.DesignatorIndex = 0
+pp_data->cap[15]->NotRange.Reserved3 = 0
+pp_data->cap[15]->NotRange.DataIndex = 15
+pp_data->cap[15]->NotRange.Reserved4 = 15
+pp_data->cap[15]->Button.LogicalMin = 0
+pp_data->cap[15]->Button.LogicalMax = 0
+pp_data->cap[15]->Units = 0
+pp_data->cap[15]->UnitsExp = 0
+
+pp_data->cap[16]->UsagePage = 0xFF01
+pp_data->cap[16]->ReportID = 0x01
+pp_data->cap[16]->BitPosition = 3
+pp_data->cap[16]->BitSize = 1
+pp_data->cap[16]->ReportCount = 1
+pp_data->cap[16]->BytePosition = 0x0007
+pp_data->cap[16]->BitCount = 1
+pp_data->cap[16]->BitField = 0x02
+pp_data->cap[16]->NextBytePosition = 0x0008
+pp_data->cap[16]->LinkCollection = 0x0001
+pp_data->cap[16]->LinkUsagePage = 0xFF01
+pp_data->cap[16]->LinkUsage = 0x0001
+pp_data->cap[16]->IsMultipleItemsForArray = 0
+pp_data->cap[16]->IsButtonCap = 1
+pp_data->cap[16]->IsPadding = 0
+pp_data->cap[16]->IsAbsolute = 1
+pp_data->cap[16]->IsRange = 0
+pp_data->cap[16]->IsAlias = 0
+pp_data->cap[16]->IsStringRange = 0
+pp_data->cap[16]->IsDesignatorRange = 0
+pp_data->cap[16]->Reserved1 = 0x000000
+pp_data->cap[16]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[16]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[16]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[16]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[16]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[16]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[16]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[16]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[16]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[16]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[16]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[16]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[16]->NotRange.Usage = 0x0002
+pp_data->cap[16]->NotRange.Reserved1 = 0x0002
+pp_data->cap[16]->NotRange.StringIndex = 0
+pp_data->cap[16]->NotRange.Reserved2 = 0
+pp_data->cap[16]->NotRange.DesignatorIndex = 0
+pp_data->cap[16]->NotRange.Reserved3 = 0
+pp_data->cap[16]->NotRange.DataIndex = 16
+pp_data->cap[16]->NotRange.Reserved4 = 16
+pp_data->cap[16]->Button.LogicalMin = 0
+pp_data->cap[16]->Button.LogicalMax = 0
+pp_data->cap[16]->Units = 0
+pp_data->cap[16]->UnitsExp = 0
+
+pp_data->cap[17]->UsagePage = 0xFF01
+pp_data->cap[17]->ReportID = 0x01
+pp_data->cap[17]->BitPosition = 2
+pp_data->cap[17]->BitSize = 1
+pp_data->cap[17]->ReportCount = 1
+pp_data->cap[17]->BytePosition = 0x0007
+pp_data->cap[17]->BitCount = 1
+pp_data->cap[17]->BitField = 0x02
+pp_data->cap[17]->NextBytePosition = 0x0008
+pp_data->cap[17]->LinkCollection = 0x0001
+pp_data->cap[17]->LinkUsagePage = 0xFF01
+pp_data->cap[17]->LinkUsage = 0x0001
+pp_data->cap[17]->IsMultipleItemsForArray = 0
+pp_data->cap[17]->IsButtonCap = 1
+pp_data->cap[17]->IsPadding = 0
+pp_data->cap[17]->IsAbsolute = 1
+pp_data->cap[17]->IsRange = 0
+pp_data->cap[17]->IsAlias = 0
+pp_data->cap[17]->IsStringRange = 0
+pp_data->cap[17]->IsDesignatorRange = 0
+pp_data->cap[17]->Reserved1 = 0x000000
+pp_data->cap[17]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[17]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[17]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[17]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[17]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[17]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[17]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[17]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[17]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[17]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[17]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[17]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[17]->NotRange.Usage = 0x0002
+pp_data->cap[17]->NotRange.Reserved1 = 0x0002
+pp_data->cap[17]->NotRange.StringIndex = 0
+pp_data->cap[17]->NotRange.Reserved2 = 0
+pp_data->cap[17]->NotRange.DesignatorIndex = 0
+pp_data->cap[17]->NotRange.Reserved3 = 0
+pp_data->cap[17]->NotRange.DataIndex = 17
+pp_data->cap[17]->NotRange.Reserved4 = 17
+pp_data->cap[17]->Button.LogicalMin = 0
+pp_data->cap[17]->Button.LogicalMax = 0
+pp_data->cap[17]->Units = 0
+pp_data->cap[17]->UnitsExp = 0
+
+pp_data->cap[18]->UsagePage = 0xFF01
+pp_data->cap[18]->ReportID = 0x01
+pp_data->cap[18]->BitPosition = 1
+pp_data->cap[18]->BitSize = 1
+pp_data->cap[18]->ReportCount = 1
+pp_data->cap[18]->BytePosition = 0x0007
+pp_data->cap[18]->BitCount = 1
+pp_data->cap[18]->BitField = 0x02
+pp_data->cap[18]->NextBytePosition = 0x0008
+pp_data->cap[18]->LinkCollection = 0x0001
+pp_data->cap[18]->LinkUsagePage = 0xFF01
+pp_data->cap[18]->LinkUsage = 0x0001
+pp_data->cap[18]->IsMultipleItemsForArray = 0
+pp_data->cap[18]->IsButtonCap = 1
+pp_data->cap[18]->IsPadding = 0
+pp_data->cap[18]->IsAbsolute = 1
+pp_data->cap[18]->IsRange = 0
+pp_data->cap[18]->IsAlias = 0
+pp_data->cap[18]->IsStringRange = 0
+pp_data->cap[18]->IsDesignatorRange = 0
+pp_data->cap[18]->Reserved1 = 0x000000
+pp_data->cap[18]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[18]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[18]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[18]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[18]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[18]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[18]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[18]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[18]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[18]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[18]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[18]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[18]->NotRange.Usage = 0x0002
+pp_data->cap[18]->NotRange.Reserved1 = 0x0002
+pp_data->cap[18]->NotRange.StringIndex = 0
+pp_data->cap[18]->NotRange.Reserved2 = 0
+pp_data->cap[18]->NotRange.DesignatorIndex = 0
+pp_data->cap[18]->NotRange.Reserved3 = 0
+pp_data->cap[18]->NotRange.DataIndex = 18
+pp_data->cap[18]->NotRange.Reserved4 = 18
+pp_data->cap[18]->Button.LogicalMin = 0
+pp_data->cap[18]->Button.LogicalMax = 0
+pp_data->cap[18]->Units = 0
+pp_data->cap[18]->UnitsExp = 0
+
+pp_data->cap[19]->UsagePage = 0xFF01
+pp_data->cap[19]->ReportID = 0x01
+pp_data->cap[19]->BitPosition = 0
+pp_data->cap[19]->BitSize = 1
+pp_data->cap[19]->ReportCount = 1
+pp_data->cap[19]->BytePosition = 0x0007
+pp_data->cap[19]->BitCount = 1
+pp_data->cap[19]->BitField = 0x02
+pp_data->cap[19]->NextBytePosition = 0x0008
+pp_data->cap[19]->LinkCollection = 0x0001
+pp_data->cap[19]->LinkUsagePage = 0xFF01
+pp_data->cap[19]->LinkUsage = 0x0001
+pp_data->cap[19]->IsMultipleItemsForArray = 0
+pp_data->cap[19]->IsButtonCap = 1
+pp_data->cap[19]->IsPadding = 0
+pp_data->cap[19]->IsAbsolute = 1
+pp_data->cap[19]->IsRange = 0
+pp_data->cap[19]->IsAlias = 0
+pp_data->cap[19]->IsStringRange = 0
+pp_data->cap[19]->IsDesignatorRange = 0
+pp_data->cap[19]->Reserved1 = 0x000000
+pp_data->cap[19]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[19]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[19]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[19]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[19]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[19]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[19]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[19]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[19]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[19]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[19]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[19]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[19]->NotRange.Usage = 0x0002
+pp_data->cap[19]->NotRange.Reserved1 = 0x0002
+pp_data->cap[19]->NotRange.StringIndex = 0
+pp_data->cap[19]->NotRange.Reserved2 = 0
+pp_data->cap[19]->NotRange.DesignatorIndex = 0
+pp_data->cap[19]->NotRange.Reserved3 = 0
+pp_data->cap[19]->NotRange.DataIndex = 19
+pp_data->cap[19]->NotRange.Reserved4 = 19
+pp_data->cap[19]->Button.LogicalMin = 0
+pp_data->cap[19]->Button.LogicalMax = 0
+pp_data->cap[19]->Units = 0
+pp_data->cap[19]->UnitsExp = 0
+
+pp_data->cap[20]->UsagePage = 0xFF01
+pp_data->cap[20]->ReportID = 0x01
+pp_data->cap[20]->BitPosition = 7
+pp_data->cap[20]->BitSize = 1
+pp_data->cap[20]->ReportCount = 1
+pp_data->cap[20]->BytePosition = 0x0006
+pp_data->cap[20]->BitCount = 1
+pp_data->cap[20]->BitField = 0x02
+pp_data->cap[20]->NextBytePosition = 0x0007
+pp_data->cap[20]->LinkCollection = 0x0001
+pp_data->cap[20]->LinkUsagePage = 0xFF01
+pp_data->cap[20]->LinkUsage = 0x0001
+pp_data->cap[20]->IsMultipleItemsForArray = 0
+pp_data->cap[20]->IsButtonCap = 1
+pp_data->cap[20]->IsPadding = 0
+pp_data->cap[20]->IsAbsolute = 1
+pp_data->cap[20]->IsRange = 0
+pp_data->cap[20]->IsAlias = 0
+pp_data->cap[20]->IsStringRange = 0
+pp_data->cap[20]->IsDesignatorRange = 0
+pp_data->cap[20]->Reserved1 = 0x000000
+pp_data->cap[20]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[20]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[20]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[20]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[20]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[20]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[20]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[20]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[20]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[20]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[20]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[20]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[20]->NotRange.Usage = 0x0002
+pp_data->cap[20]->NotRange.Reserved1 = 0x0002
+pp_data->cap[20]->NotRange.StringIndex = 0
+pp_data->cap[20]->NotRange.Reserved2 = 0
+pp_data->cap[20]->NotRange.DesignatorIndex = 0
+pp_data->cap[20]->NotRange.Reserved3 = 0
+pp_data->cap[20]->NotRange.DataIndex = 20
+pp_data->cap[20]->NotRange.Reserved4 = 20
+pp_data->cap[20]->Button.LogicalMin = 0
+pp_data->cap[20]->Button.LogicalMax = 0
+pp_data->cap[20]->Units = 0
+pp_data->cap[20]->UnitsExp = 0
+
+pp_data->cap[21]->UsagePage = 0xFF01
+pp_data->cap[21]->ReportID = 0x01
+pp_data->cap[21]->BitPosition = 6
+pp_data->cap[21]->BitSize = 1
+pp_data->cap[21]->ReportCount = 1
+pp_data->cap[21]->BytePosition = 0x0006
+pp_data->cap[21]->BitCount = 1
+pp_data->cap[21]->BitField = 0x02
+pp_data->cap[21]->NextBytePosition = 0x0007
+pp_data->cap[21]->LinkCollection = 0x0001
+pp_data->cap[21]->LinkUsagePage = 0xFF01
+pp_data->cap[21]->LinkUsage = 0x0001
+pp_data->cap[21]->IsMultipleItemsForArray = 0
+pp_data->cap[21]->IsButtonCap = 1
+pp_data->cap[21]->IsPadding = 0
+pp_data->cap[21]->IsAbsolute = 1
+pp_data->cap[21]->IsRange = 0
+pp_data->cap[21]->IsAlias = 0
+pp_data->cap[21]->IsStringRange = 0
+pp_data->cap[21]->IsDesignatorRange = 0
+pp_data->cap[21]->Reserved1 = 0x000000
+pp_data->cap[21]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[21]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[21]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[21]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[21]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[21]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[21]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[21]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[21]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[21]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[21]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[21]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[21]->NotRange.Usage = 0x0002
+pp_data->cap[21]->NotRange.Reserved1 = 0x0002
+pp_data->cap[21]->NotRange.StringIndex = 0
+pp_data->cap[21]->NotRange.Reserved2 = 0
+pp_data->cap[21]->NotRange.DesignatorIndex = 0
+pp_data->cap[21]->NotRange.Reserved3 = 0
+pp_data->cap[21]->NotRange.DataIndex = 21
+pp_data->cap[21]->NotRange.Reserved4 = 21
+pp_data->cap[21]->Button.LogicalMin = 0
+pp_data->cap[21]->Button.LogicalMax = 0
+pp_data->cap[21]->Units = 0
+pp_data->cap[21]->UnitsExp = 0
+
+pp_data->cap[22]->UsagePage = 0xFF01
+pp_data->cap[22]->ReportID = 0x01
+pp_data->cap[22]->BitPosition = 5
+pp_data->cap[22]->BitSize = 1
+pp_data->cap[22]->ReportCount = 1
+pp_data->cap[22]->BytePosition = 0x0006
+pp_data->cap[22]->BitCount = 1
+pp_data->cap[22]->BitField = 0x02
+pp_data->cap[22]->NextBytePosition = 0x0007
+pp_data->cap[22]->LinkCollection = 0x0001
+pp_data->cap[22]->LinkUsagePage = 0xFF01
+pp_data->cap[22]->LinkUsage = 0x0001
+pp_data->cap[22]->IsMultipleItemsForArray = 0
+pp_data->cap[22]->IsButtonCap = 1
+pp_data->cap[22]->IsPadding = 0
+pp_data->cap[22]->IsAbsolute = 1
+pp_data->cap[22]->IsRange = 0
+pp_data->cap[22]->IsAlias = 0
+pp_data->cap[22]->IsStringRange = 0
+pp_data->cap[22]->IsDesignatorRange = 0
+pp_data->cap[22]->Reserved1 = 0x000000
+pp_data->cap[22]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[22]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[22]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[22]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[22]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[22]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[22]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[22]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[22]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[22]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[22]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[22]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[22]->NotRange.Usage = 0x0002
+pp_data->cap[22]->NotRange.Reserved1 = 0x0002
+pp_data->cap[22]->NotRange.StringIndex = 0
+pp_data->cap[22]->NotRange.Reserved2 = 0
+pp_data->cap[22]->NotRange.DesignatorIndex = 0
+pp_data->cap[22]->NotRange.Reserved3 = 0
+pp_data->cap[22]->NotRange.DataIndex = 22
+pp_data->cap[22]->NotRange.Reserved4 = 22
+pp_data->cap[22]->Button.LogicalMin = 0
+pp_data->cap[22]->Button.LogicalMax = 0
+pp_data->cap[22]->Units = 0
+pp_data->cap[22]->UnitsExp = 0
+
+pp_data->cap[23]->UsagePage = 0xFF01
+pp_data->cap[23]->ReportID = 0x01
+pp_data->cap[23]->BitPosition = 4
+pp_data->cap[23]->BitSize = 1
+pp_data->cap[23]->ReportCount = 1
+pp_data->cap[23]->BytePosition = 0x0006
+pp_data->cap[23]->BitCount = 1
+pp_data->cap[23]->BitField = 0x02
+pp_data->cap[23]->NextBytePosition = 0x0007
+pp_data->cap[23]->LinkCollection = 0x0001
+pp_data->cap[23]->LinkUsagePage = 0xFF01
+pp_data->cap[23]->LinkUsage = 0x0001
+pp_data->cap[23]->IsMultipleItemsForArray = 0
+pp_data->cap[23]->IsButtonCap = 1
+pp_data->cap[23]->IsPadding = 0
+pp_data->cap[23]->IsAbsolute = 1
+pp_data->cap[23]->IsRange = 0
+pp_data->cap[23]->IsAlias = 0
+pp_data->cap[23]->IsStringRange = 0
+pp_data->cap[23]->IsDesignatorRange = 0
+pp_data->cap[23]->Reserved1 = 0x000000
+pp_data->cap[23]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[23]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[23]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[23]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[23]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[23]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[23]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[23]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[23]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[23]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[23]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[23]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[23]->NotRange.Usage = 0x0002
+pp_data->cap[23]->NotRange.Reserved1 = 0x0002
+pp_data->cap[23]->NotRange.StringIndex = 0
+pp_data->cap[23]->NotRange.Reserved2 = 0
+pp_data->cap[23]->NotRange.DesignatorIndex = 0
+pp_data->cap[23]->NotRange.Reserved3 = 0
+pp_data->cap[23]->NotRange.DataIndex = 23
+pp_data->cap[23]->NotRange.Reserved4 = 23
+pp_data->cap[23]->Button.LogicalMin = 0
+pp_data->cap[23]->Button.LogicalMax = 0
+pp_data->cap[23]->Units = 0
+pp_data->cap[23]->UnitsExp = 0
+
+pp_data->cap[24]->UsagePage = 0xFF01
+pp_data->cap[24]->ReportID = 0x01
+pp_data->cap[24]->BitPosition = 3
+pp_data->cap[24]->BitSize = 1
+pp_data->cap[24]->ReportCount = 1
+pp_data->cap[24]->BytePosition = 0x0006
+pp_data->cap[24]->BitCount = 1
+pp_data->cap[24]->BitField = 0x02
+pp_data->cap[24]->NextBytePosition = 0x0007
+pp_data->cap[24]->LinkCollection = 0x0001
+pp_data->cap[24]->LinkUsagePage = 0xFF01
+pp_data->cap[24]->LinkUsage = 0x0001
+pp_data->cap[24]->IsMultipleItemsForArray = 0
+pp_data->cap[24]->IsButtonCap = 1
+pp_data->cap[24]->IsPadding = 0
+pp_data->cap[24]->IsAbsolute = 1
+pp_data->cap[24]->IsRange = 0
+pp_data->cap[24]->IsAlias = 0
+pp_data->cap[24]->IsStringRange = 0
+pp_data->cap[24]->IsDesignatorRange = 0
+pp_data->cap[24]->Reserved1 = 0x000000
+pp_data->cap[24]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[24]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[24]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[24]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[24]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[24]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[24]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[24]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[24]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[24]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[24]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[24]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[24]->NotRange.Usage = 0x0002
+pp_data->cap[24]->NotRange.Reserved1 = 0x0002
+pp_data->cap[24]->NotRange.StringIndex = 0
+pp_data->cap[24]->NotRange.Reserved2 = 0
+pp_data->cap[24]->NotRange.DesignatorIndex = 0
+pp_data->cap[24]->NotRange.Reserved3 = 0
+pp_data->cap[24]->NotRange.DataIndex = 24
+pp_data->cap[24]->NotRange.Reserved4 = 24
+pp_data->cap[24]->Button.LogicalMin = 0
+pp_data->cap[24]->Button.LogicalMax = 0
+pp_data->cap[24]->Units = 0
+pp_data->cap[24]->UnitsExp = 0
+
+pp_data->cap[25]->UsagePage = 0xFF01
+pp_data->cap[25]->ReportID = 0x01
+pp_data->cap[25]->BitPosition = 2
+pp_data->cap[25]->BitSize = 1
+pp_data->cap[25]->ReportCount = 1
+pp_data->cap[25]->BytePosition = 0x0006
+pp_data->cap[25]->BitCount = 1
+pp_data->cap[25]->BitField = 0x02
+pp_data->cap[25]->NextBytePosition = 0x0007
+pp_data->cap[25]->LinkCollection = 0x0001
+pp_data->cap[25]->LinkUsagePage = 0xFF01
+pp_data->cap[25]->LinkUsage = 0x0001
+pp_data->cap[25]->IsMultipleItemsForArray = 0
+pp_data->cap[25]->IsButtonCap = 1
+pp_data->cap[25]->IsPadding = 0
+pp_data->cap[25]->IsAbsolute = 1
+pp_data->cap[25]->IsRange = 0
+pp_data->cap[25]->IsAlias = 0
+pp_data->cap[25]->IsStringRange = 0
+pp_data->cap[25]->IsDesignatorRange = 0
+pp_data->cap[25]->Reserved1 = 0x000000
+pp_data->cap[25]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[25]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[25]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[25]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[25]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[25]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[25]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[25]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[25]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[25]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[25]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[25]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[25]->NotRange.Usage = 0x0002
+pp_data->cap[25]->NotRange.Reserved1 = 0x0002
+pp_data->cap[25]->NotRange.StringIndex = 0
+pp_data->cap[25]->NotRange.Reserved2 = 0
+pp_data->cap[25]->NotRange.DesignatorIndex = 0
+pp_data->cap[25]->NotRange.Reserved3 = 0
+pp_data->cap[25]->NotRange.DataIndex = 25
+pp_data->cap[25]->NotRange.Reserved4 = 25
+pp_data->cap[25]->Button.LogicalMin = 0
+pp_data->cap[25]->Button.LogicalMax = 0
+pp_data->cap[25]->Units = 0
+pp_data->cap[25]->UnitsExp = 0
+
+pp_data->cap[26]->UsagePage = 0xFF01
+pp_data->cap[26]->ReportID = 0x01
+pp_data->cap[26]->BitPosition = 1
+pp_data->cap[26]->BitSize = 1
+pp_data->cap[26]->ReportCount = 1
+pp_data->cap[26]->BytePosition = 0x0006
+pp_data->cap[26]->BitCount = 1
+pp_data->cap[26]->BitField = 0x02
+pp_data->cap[26]->NextBytePosition = 0x0007
+pp_data->cap[26]->LinkCollection = 0x0001
+pp_data->cap[26]->LinkUsagePage = 0xFF01
+pp_data->cap[26]->LinkUsage = 0x0001
+pp_data->cap[26]->IsMultipleItemsForArray = 0
+pp_data->cap[26]->IsButtonCap = 1
+pp_data->cap[26]->IsPadding = 0
+pp_data->cap[26]->IsAbsolute = 1
+pp_data->cap[26]->IsRange = 0
+pp_data->cap[26]->IsAlias = 0
+pp_data->cap[26]->IsStringRange = 0
+pp_data->cap[26]->IsDesignatorRange = 0
+pp_data->cap[26]->Reserved1 = 0x000000
+pp_data->cap[26]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[26]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[26]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[26]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[26]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[26]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[26]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[26]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[26]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[26]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[26]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[26]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[26]->NotRange.Usage = 0x0002
+pp_data->cap[26]->NotRange.Reserved1 = 0x0002
+pp_data->cap[26]->NotRange.StringIndex = 0
+pp_data->cap[26]->NotRange.Reserved2 = 0
+pp_data->cap[26]->NotRange.DesignatorIndex = 0
+pp_data->cap[26]->NotRange.Reserved3 = 0
+pp_data->cap[26]->NotRange.DataIndex = 26
+pp_data->cap[26]->NotRange.Reserved4 = 26
+pp_data->cap[26]->Button.LogicalMin = 0
+pp_data->cap[26]->Button.LogicalMax = 0
+pp_data->cap[26]->Units = 0
+pp_data->cap[26]->UnitsExp = 0
+
+pp_data->cap[27]->UsagePage = 0xFF01
+pp_data->cap[27]->ReportID = 0x01
+pp_data->cap[27]->BitPosition = 0
+pp_data->cap[27]->BitSize = 1
+pp_data->cap[27]->ReportCount = 1
+pp_data->cap[27]->BytePosition = 0x0006
+pp_data->cap[27]->BitCount = 1
+pp_data->cap[27]->BitField = 0x02
+pp_data->cap[27]->NextBytePosition = 0x0007
+pp_data->cap[27]->LinkCollection = 0x0001
+pp_data->cap[27]->LinkUsagePage = 0xFF01
+pp_data->cap[27]->LinkUsage = 0x0001
+pp_data->cap[27]->IsMultipleItemsForArray = 0
+pp_data->cap[27]->IsButtonCap = 1
+pp_data->cap[27]->IsPadding = 0
+pp_data->cap[27]->IsAbsolute = 1
+pp_data->cap[27]->IsRange = 0
+pp_data->cap[27]->IsAlias = 0
+pp_data->cap[27]->IsStringRange = 0
+pp_data->cap[27]->IsDesignatorRange = 0
+pp_data->cap[27]->Reserved1 = 0x000000
+pp_data->cap[27]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[27]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[27]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[27]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[27]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[27]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[27]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[27]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[27]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[27]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[27]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[27]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[27]->NotRange.Usage = 0x0002
+pp_data->cap[27]->NotRange.Reserved1 = 0x0002
+pp_data->cap[27]->NotRange.StringIndex = 0
+pp_data->cap[27]->NotRange.Reserved2 = 0
+pp_data->cap[27]->NotRange.DesignatorIndex = 0
+pp_data->cap[27]->NotRange.Reserved3 = 0
+pp_data->cap[27]->NotRange.DataIndex = 27
+pp_data->cap[27]->NotRange.Reserved4 = 27
+pp_data->cap[27]->Button.LogicalMin = 0
+pp_data->cap[27]->Button.LogicalMax = 0
+pp_data->cap[27]->Units = 0
+pp_data->cap[27]->UnitsExp = 0
+
+pp_data->cap[28]->UsagePage = 0xFF01
+pp_data->cap[28]->ReportID = 0x01
+pp_data->cap[28]->BitPosition = 7
+pp_data->cap[28]->BitSize = 1
+pp_data->cap[28]->ReportCount = 1
+pp_data->cap[28]->BytePosition = 0x0005
+pp_data->cap[28]->BitCount = 1
+pp_data->cap[28]->BitField = 0x02
+pp_data->cap[28]->NextBytePosition = 0x0006
+pp_data->cap[28]->LinkCollection = 0x0001
+pp_data->cap[28]->LinkUsagePage = 0xFF01
+pp_data->cap[28]->LinkUsage = 0x0001
+pp_data->cap[28]->IsMultipleItemsForArray = 0
+pp_data->cap[28]->IsButtonCap = 1
+pp_data->cap[28]->IsPadding = 0
+pp_data->cap[28]->IsAbsolute = 1
+pp_data->cap[28]->IsRange = 0
+pp_data->cap[28]->IsAlias = 0
+pp_data->cap[28]->IsStringRange = 0
+pp_data->cap[28]->IsDesignatorRange = 0
+pp_data->cap[28]->Reserved1 = 0x000000
+pp_data->cap[28]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[28]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[28]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[28]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[28]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[28]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[28]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[28]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[28]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[28]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[28]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[28]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[28]->NotRange.Usage = 0x0002
+pp_data->cap[28]->NotRange.Reserved1 = 0x0002
+pp_data->cap[28]->NotRange.StringIndex = 0
+pp_data->cap[28]->NotRange.Reserved2 = 0
+pp_data->cap[28]->NotRange.DesignatorIndex = 0
+pp_data->cap[28]->NotRange.Reserved3 = 0
+pp_data->cap[28]->NotRange.DataIndex = 28
+pp_data->cap[28]->NotRange.Reserved4 = 28
+pp_data->cap[28]->Button.LogicalMin = 0
+pp_data->cap[28]->Button.LogicalMax = 0
+pp_data->cap[28]->Units = 0
+pp_data->cap[28]->UnitsExp = 0
+
+pp_data->cap[29]->UsagePage = 0xFF01
+pp_data->cap[29]->ReportID = 0x01
+pp_data->cap[29]->BitPosition = 6
+pp_data->cap[29]->BitSize = 1
+pp_data->cap[29]->ReportCount = 1
+pp_data->cap[29]->BytePosition = 0x0005
+pp_data->cap[29]->BitCount = 1
+pp_data->cap[29]->BitField = 0x02
+pp_data->cap[29]->NextBytePosition = 0x0006
+pp_data->cap[29]->LinkCollection = 0x0001
+pp_data->cap[29]->LinkUsagePage = 0xFF01
+pp_data->cap[29]->LinkUsage = 0x0001
+pp_data->cap[29]->IsMultipleItemsForArray = 0
+pp_data->cap[29]->IsButtonCap = 1
+pp_data->cap[29]->IsPadding = 0
+pp_data->cap[29]->IsAbsolute = 1
+pp_data->cap[29]->IsRange = 0
+pp_data->cap[29]->IsAlias = 0
+pp_data->cap[29]->IsStringRange = 0
+pp_data->cap[29]->IsDesignatorRange = 0
+pp_data->cap[29]->Reserved1 = 0x000000
+pp_data->cap[29]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[29]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[29]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[29]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[29]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[29]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[29]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[29]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[29]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[29]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[29]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[29]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[29]->NotRange.Usage = 0x0002
+pp_data->cap[29]->NotRange.Reserved1 = 0x0002
+pp_data->cap[29]->NotRange.StringIndex = 0
+pp_data->cap[29]->NotRange.Reserved2 = 0
+pp_data->cap[29]->NotRange.DesignatorIndex = 0
+pp_data->cap[29]->NotRange.Reserved3 = 0
+pp_data->cap[29]->NotRange.DataIndex = 29
+pp_data->cap[29]->NotRange.Reserved4 = 29
+pp_data->cap[29]->Button.LogicalMin = 0
+pp_data->cap[29]->Button.LogicalMax = 0
+pp_data->cap[29]->Units = 0
+pp_data->cap[29]->UnitsExp = 0
+
+pp_data->cap[30]->UsagePage = 0xFF01
+pp_data->cap[30]->ReportID = 0x01
+pp_data->cap[30]->BitPosition = 5
+pp_data->cap[30]->BitSize = 1
+pp_data->cap[30]->ReportCount = 1
+pp_data->cap[30]->BytePosition = 0x0005
+pp_data->cap[30]->BitCount = 1
+pp_data->cap[30]->BitField = 0x02
+pp_data->cap[30]->NextBytePosition = 0x0006
+pp_data->cap[30]->LinkCollection = 0x0001
+pp_data->cap[30]->LinkUsagePage = 0xFF01
+pp_data->cap[30]->LinkUsage = 0x0001
+pp_data->cap[30]->IsMultipleItemsForArray = 0
+pp_data->cap[30]->IsButtonCap = 1
+pp_data->cap[30]->IsPadding = 0
+pp_data->cap[30]->IsAbsolute = 1
+pp_data->cap[30]->IsRange = 0
+pp_data->cap[30]->IsAlias = 0
+pp_data->cap[30]->IsStringRange = 0
+pp_data->cap[30]->IsDesignatorRange = 0
+pp_data->cap[30]->Reserved1 = 0x000000
+pp_data->cap[30]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[30]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[30]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[30]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[30]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[30]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[30]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[30]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[30]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[30]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[30]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[30]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[30]->NotRange.Usage = 0x0002
+pp_data->cap[30]->NotRange.Reserved1 = 0x0002
+pp_data->cap[30]->NotRange.StringIndex = 0
+pp_data->cap[30]->NotRange.Reserved2 = 0
+pp_data->cap[30]->NotRange.DesignatorIndex = 0
+pp_data->cap[30]->NotRange.Reserved3 = 0
+pp_data->cap[30]->NotRange.DataIndex = 30
+pp_data->cap[30]->NotRange.Reserved4 = 30
+pp_data->cap[30]->Button.LogicalMin = 0
+pp_data->cap[30]->Button.LogicalMax = 0
+pp_data->cap[30]->Units = 0
+pp_data->cap[30]->UnitsExp = 0
+
+pp_data->cap[31]->UsagePage = 0xFF01
+pp_data->cap[31]->ReportID = 0x01
+pp_data->cap[31]->BitPosition = 4
+pp_data->cap[31]->BitSize = 1
+pp_data->cap[31]->ReportCount = 1
+pp_data->cap[31]->BytePosition = 0x0005
+pp_data->cap[31]->BitCount = 1
+pp_data->cap[31]->BitField = 0x02
+pp_data->cap[31]->NextBytePosition = 0x0006
+pp_data->cap[31]->LinkCollection = 0x0001
+pp_data->cap[31]->LinkUsagePage = 0xFF01
+pp_data->cap[31]->LinkUsage = 0x0001
+pp_data->cap[31]->IsMultipleItemsForArray = 0
+pp_data->cap[31]->IsButtonCap = 1
+pp_data->cap[31]->IsPadding = 0
+pp_data->cap[31]->IsAbsolute = 1
+pp_data->cap[31]->IsRange = 0
+pp_data->cap[31]->IsAlias = 0
+pp_data->cap[31]->IsStringRange = 0
+pp_data->cap[31]->IsDesignatorRange = 0
+pp_data->cap[31]->Reserved1 = 0x000000
+pp_data->cap[31]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[31]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[31]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[31]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[31]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[31]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[31]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[31]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[31]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[31]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[31]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[31]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[31]->NotRange.Usage = 0x0002
+pp_data->cap[31]->NotRange.Reserved1 = 0x0002
+pp_data->cap[31]->NotRange.StringIndex = 0
+pp_data->cap[31]->NotRange.Reserved2 = 0
+pp_data->cap[31]->NotRange.DesignatorIndex = 0
+pp_data->cap[31]->NotRange.Reserved3 = 0
+pp_data->cap[31]->NotRange.DataIndex = 31
+pp_data->cap[31]->NotRange.Reserved4 = 31
+pp_data->cap[31]->Button.LogicalMin = 0
+pp_data->cap[31]->Button.LogicalMax = 0
+pp_data->cap[31]->Units = 0
+pp_data->cap[31]->UnitsExp = 0
+
+pp_data->cap[32]->UsagePage = 0xFF01
+pp_data->cap[32]->ReportID = 0x01
+pp_data->cap[32]->BitPosition = 3
+pp_data->cap[32]->BitSize = 1
+pp_data->cap[32]->ReportCount = 1
+pp_data->cap[32]->BytePosition = 0x0005
+pp_data->cap[32]->BitCount = 1
+pp_data->cap[32]->BitField = 0x02
+pp_data->cap[32]->NextBytePosition = 0x0006
+pp_data->cap[32]->LinkCollection = 0x0001
+pp_data->cap[32]->LinkUsagePage = 0xFF01
+pp_data->cap[32]->LinkUsage = 0x0001
+pp_data->cap[32]->IsMultipleItemsForArray = 0
+pp_data->cap[32]->IsButtonCap = 1
+pp_data->cap[32]->IsPadding = 0
+pp_data->cap[32]->IsAbsolute = 1
+pp_data->cap[32]->IsRange = 0
+pp_data->cap[32]->IsAlias = 0
+pp_data->cap[32]->IsStringRange = 0
+pp_data->cap[32]->IsDesignatorRange = 0
+pp_data->cap[32]->Reserved1 = 0x000000
+pp_data->cap[32]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[32]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[32]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[32]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[32]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[32]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[32]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[32]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[32]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[32]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[32]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[32]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[32]->NotRange.Usage = 0x0002
+pp_data->cap[32]->NotRange.Reserved1 = 0x0002
+pp_data->cap[32]->NotRange.StringIndex = 0
+pp_data->cap[32]->NotRange.Reserved2 = 0
+pp_data->cap[32]->NotRange.DesignatorIndex = 0
+pp_data->cap[32]->NotRange.Reserved3 = 0
+pp_data->cap[32]->NotRange.DataIndex = 32
+pp_data->cap[32]->NotRange.Reserved4 = 32
+pp_data->cap[32]->Button.LogicalMin = 0
+pp_data->cap[32]->Button.LogicalMax = 0
+pp_data->cap[32]->Units = 0
+pp_data->cap[32]->UnitsExp = 0
+
+pp_data->cap[33]->UsagePage = 0xFF01
+pp_data->cap[33]->ReportID = 0x01
+pp_data->cap[33]->BitPosition = 2
+pp_data->cap[33]->BitSize = 1
+pp_data->cap[33]->ReportCount = 1
+pp_data->cap[33]->BytePosition = 0x0005
+pp_data->cap[33]->BitCount = 1
+pp_data->cap[33]->BitField = 0x02
+pp_data->cap[33]->NextBytePosition = 0x0006
+pp_data->cap[33]->LinkCollection = 0x0001
+pp_data->cap[33]->LinkUsagePage = 0xFF01
+pp_data->cap[33]->LinkUsage = 0x0001
+pp_data->cap[33]->IsMultipleItemsForArray = 0
+pp_data->cap[33]->IsButtonCap = 1
+pp_data->cap[33]->IsPadding = 0
+pp_data->cap[33]->IsAbsolute = 1
+pp_data->cap[33]->IsRange = 0
+pp_data->cap[33]->IsAlias = 0
+pp_data->cap[33]->IsStringRange = 0
+pp_data->cap[33]->IsDesignatorRange = 0
+pp_data->cap[33]->Reserved1 = 0x000000
+pp_data->cap[33]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[33]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[33]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[33]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[33]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[33]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[33]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[33]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[33]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[33]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[33]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[33]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[33]->NotRange.Usage = 0x0002
+pp_data->cap[33]->NotRange.Reserved1 = 0x0002
+pp_data->cap[33]->NotRange.StringIndex = 0
+pp_data->cap[33]->NotRange.Reserved2 = 0
+pp_data->cap[33]->NotRange.DesignatorIndex = 0
+pp_data->cap[33]->NotRange.Reserved3 = 0
+pp_data->cap[33]->NotRange.DataIndex = 33
+pp_data->cap[33]->NotRange.Reserved4 = 33
+pp_data->cap[33]->Button.LogicalMin = 0
+pp_data->cap[33]->Button.LogicalMax = 0
+pp_data->cap[33]->Units = 0
+pp_data->cap[33]->UnitsExp = 0
+
+pp_data->cap[34]->UsagePage = 0xFF01
+pp_data->cap[34]->ReportID = 0x01
+pp_data->cap[34]->BitPosition = 1
+pp_data->cap[34]->BitSize = 1
+pp_data->cap[34]->ReportCount = 1
+pp_data->cap[34]->BytePosition = 0x0005
+pp_data->cap[34]->BitCount = 1
+pp_data->cap[34]->BitField = 0x02
+pp_data->cap[34]->NextBytePosition = 0x0006
+pp_data->cap[34]->LinkCollection = 0x0001
+pp_data->cap[34]->LinkUsagePage = 0xFF01
+pp_data->cap[34]->LinkUsage = 0x0001
+pp_data->cap[34]->IsMultipleItemsForArray = 0
+pp_data->cap[34]->IsButtonCap = 1
+pp_data->cap[34]->IsPadding = 0
+pp_data->cap[34]->IsAbsolute = 1
+pp_data->cap[34]->IsRange = 0
+pp_data->cap[34]->IsAlias = 0
+pp_data->cap[34]->IsStringRange = 0
+pp_data->cap[34]->IsDesignatorRange = 0
+pp_data->cap[34]->Reserved1 = 0x000000
+pp_data->cap[34]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[34]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[34]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[34]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[34]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[34]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[34]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[34]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[34]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[34]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[34]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[34]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[34]->NotRange.Usage = 0x0002
+pp_data->cap[34]->NotRange.Reserved1 = 0x0002
+pp_data->cap[34]->NotRange.StringIndex = 0
+pp_data->cap[34]->NotRange.Reserved2 = 0
+pp_data->cap[34]->NotRange.DesignatorIndex = 0
+pp_data->cap[34]->NotRange.Reserved3 = 0
+pp_data->cap[34]->NotRange.DataIndex = 34
+pp_data->cap[34]->NotRange.Reserved4 = 34
+pp_data->cap[34]->Button.LogicalMin = 0
+pp_data->cap[34]->Button.LogicalMax = 0
+pp_data->cap[34]->Units = 0
+pp_data->cap[34]->UnitsExp = 0
+
+pp_data->cap[35]->UsagePage = 0xFF01
+pp_data->cap[35]->ReportID = 0x01
+pp_data->cap[35]->BitPosition = 0
+pp_data->cap[35]->BitSize = 1
+pp_data->cap[35]->ReportCount = 1
+pp_data->cap[35]->BytePosition = 0x0005
+pp_data->cap[35]->BitCount = 1
+pp_data->cap[35]->BitField = 0x02
+pp_data->cap[35]->NextBytePosition = 0x0006
+pp_data->cap[35]->LinkCollection = 0x0001
+pp_data->cap[35]->LinkUsagePage = 0xFF01
+pp_data->cap[35]->LinkUsage = 0x0001
+pp_data->cap[35]->IsMultipleItemsForArray = 0
+pp_data->cap[35]->IsButtonCap = 1
+pp_data->cap[35]->IsPadding = 0
+pp_data->cap[35]->IsAbsolute = 1
+pp_data->cap[35]->IsRange = 0
+pp_data->cap[35]->IsAlias = 0
+pp_data->cap[35]->IsStringRange = 0
+pp_data->cap[35]->IsDesignatorRange = 0
+pp_data->cap[35]->Reserved1 = 0x000000
+pp_data->cap[35]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[35]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[35]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[35]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[35]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[35]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[35]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[35]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[35]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[35]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[35]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[35]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[35]->NotRange.Usage = 0x0002
+pp_data->cap[35]->NotRange.Reserved1 = 0x0002
+pp_data->cap[35]->NotRange.StringIndex = 0
+pp_data->cap[35]->NotRange.Reserved2 = 0
+pp_data->cap[35]->NotRange.DesignatorIndex = 0
+pp_data->cap[35]->NotRange.Reserved3 = 0
+pp_data->cap[35]->NotRange.DataIndex = 35
+pp_data->cap[35]->NotRange.Reserved4 = 35
+pp_data->cap[35]->Button.LogicalMin = 0
+pp_data->cap[35]->Button.LogicalMax = 0
+pp_data->cap[35]->Units = 0
+pp_data->cap[35]->UnitsExp = 0
+
+pp_data->cap[36]->UsagePage = 0xFF01
+pp_data->cap[36]->ReportID = 0x01
+pp_data->cap[36]->BitPosition = 7
+pp_data->cap[36]->BitSize = 1
+pp_data->cap[36]->ReportCount = 1
+pp_data->cap[36]->BytePosition = 0x0004
+pp_data->cap[36]->BitCount = 1
+pp_data->cap[36]->BitField = 0x02
+pp_data->cap[36]->NextBytePosition = 0x0005
+pp_data->cap[36]->LinkCollection = 0x0001
+pp_data->cap[36]->LinkUsagePage = 0xFF01
+pp_data->cap[36]->LinkUsage = 0x0001
+pp_data->cap[36]->IsMultipleItemsForArray = 0
+pp_data->cap[36]->IsButtonCap = 1
+pp_data->cap[36]->IsPadding = 0
+pp_data->cap[36]->IsAbsolute = 1
+pp_data->cap[36]->IsRange = 0
+pp_data->cap[36]->IsAlias = 0
+pp_data->cap[36]->IsStringRange = 0
+pp_data->cap[36]->IsDesignatorRange = 0
+pp_data->cap[36]->Reserved1 = 0x000000
+pp_data->cap[36]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[36]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[36]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[36]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[36]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[36]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[36]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[36]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[36]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[36]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[36]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[36]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[36]->NotRange.Usage = 0x0002
+pp_data->cap[36]->NotRange.Reserved1 = 0x0002
+pp_data->cap[36]->NotRange.StringIndex = 0
+pp_data->cap[36]->NotRange.Reserved2 = 0
+pp_data->cap[36]->NotRange.DesignatorIndex = 0
+pp_data->cap[36]->NotRange.Reserved3 = 0
+pp_data->cap[36]->NotRange.DataIndex = 36
+pp_data->cap[36]->NotRange.Reserved4 = 36
+pp_data->cap[36]->Button.LogicalMin = 0
+pp_data->cap[36]->Button.LogicalMax = 0
+pp_data->cap[36]->Units = 0
+pp_data->cap[36]->UnitsExp = 0
+
+pp_data->cap[37]->UsagePage = 0xFF01
+pp_data->cap[37]->ReportID = 0x01
+pp_data->cap[37]->BitPosition = 6
+pp_data->cap[37]->BitSize = 1
+pp_data->cap[37]->ReportCount = 1
+pp_data->cap[37]->BytePosition = 0x0004
+pp_data->cap[37]->BitCount = 1
+pp_data->cap[37]->BitField = 0x02
+pp_data->cap[37]->NextBytePosition = 0x0005
+pp_data->cap[37]->LinkCollection = 0x0001
+pp_data->cap[37]->LinkUsagePage = 0xFF01
+pp_data->cap[37]->LinkUsage = 0x0001
+pp_data->cap[37]->IsMultipleItemsForArray = 0
+pp_data->cap[37]->IsButtonCap = 1
+pp_data->cap[37]->IsPadding = 0
+pp_data->cap[37]->IsAbsolute = 1
+pp_data->cap[37]->IsRange = 0
+pp_data->cap[37]->IsAlias = 0
+pp_data->cap[37]->IsStringRange = 0
+pp_data->cap[37]->IsDesignatorRange = 0
+pp_data->cap[37]->Reserved1 = 0x000000
+pp_data->cap[37]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[37]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[37]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[37]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[37]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[37]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[37]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[37]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[37]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[37]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[37]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[37]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[37]->NotRange.Usage = 0x0002
+pp_data->cap[37]->NotRange.Reserved1 = 0x0002
+pp_data->cap[37]->NotRange.StringIndex = 0
+pp_data->cap[37]->NotRange.Reserved2 = 0
+pp_data->cap[37]->NotRange.DesignatorIndex = 0
+pp_data->cap[37]->NotRange.Reserved3 = 0
+pp_data->cap[37]->NotRange.DataIndex = 37
+pp_data->cap[37]->NotRange.Reserved4 = 37
+pp_data->cap[37]->Button.LogicalMin = 0
+pp_data->cap[37]->Button.LogicalMax = 0
+pp_data->cap[37]->Units = 0
+pp_data->cap[37]->UnitsExp = 0
+
+pp_data->cap[38]->UsagePage = 0xFF01
+pp_data->cap[38]->ReportID = 0x01
+pp_data->cap[38]->BitPosition = 5
+pp_data->cap[38]->BitSize = 1
+pp_data->cap[38]->ReportCount = 1
+pp_data->cap[38]->BytePosition = 0x0004
+pp_data->cap[38]->BitCount = 1
+pp_data->cap[38]->BitField = 0x02
+pp_data->cap[38]->NextBytePosition = 0x0005
+pp_data->cap[38]->LinkCollection = 0x0001
+pp_data->cap[38]->LinkUsagePage = 0xFF01
+pp_data->cap[38]->LinkUsage = 0x0001
+pp_data->cap[38]->IsMultipleItemsForArray = 0
+pp_data->cap[38]->IsButtonCap = 1
+pp_data->cap[38]->IsPadding = 0
+pp_data->cap[38]->IsAbsolute = 1
+pp_data->cap[38]->IsRange = 0
+pp_data->cap[38]->IsAlias = 0
+pp_data->cap[38]->IsStringRange = 0
+pp_data->cap[38]->IsDesignatorRange = 0
+pp_data->cap[38]->Reserved1 = 0x000000
+pp_data->cap[38]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[38]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[38]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[38]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[38]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[38]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[38]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[38]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[38]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[38]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[38]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[38]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[38]->NotRange.Usage = 0x0002
+pp_data->cap[38]->NotRange.Reserved1 = 0x0002
+pp_data->cap[38]->NotRange.StringIndex = 0
+pp_data->cap[38]->NotRange.Reserved2 = 0
+pp_data->cap[38]->NotRange.DesignatorIndex = 0
+pp_data->cap[38]->NotRange.Reserved3 = 0
+pp_data->cap[38]->NotRange.DataIndex = 38
+pp_data->cap[38]->NotRange.Reserved4 = 38
+pp_data->cap[38]->Button.LogicalMin = 0
+pp_data->cap[38]->Button.LogicalMax = 0
+pp_data->cap[38]->Units = 0
+pp_data->cap[38]->UnitsExp = 0
+
+pp_data->cap[39]->UsagePage = 0xFF01
+pp_data->cap[39]->ReportID = 0x01
+pp_data->cap[39]->BitPosition = 4
+pp_data->cap[39]->BitSize = 1
+pp_data->cap[39]->ReportCount = 1
+pp_data->cap[39]->BytePosition = 0x0004
+pp_data->cap[39]->BitCount = 1
+pp_data->cap[39]->BitField = 0x02
+pp_data->cap[39]->NextBytePosition = 0x0005
+pp_data->cap[39]->LinkCollection = 0x0001
+pp_data->cap[39]->LinkUsagePage = 0xFF01
+pp_data->cap[39]->LinkUsage = 0x0001
+pp_data->cap[39]->IsMultipleItemsForArray = 0
+pp_data->cap[39]->IsButtonCap = 1
+pp_data->cap[39]->IsPadding = 0
+pp_data->cap[39]->IsAbsolute = 1
+pp_data->cap[39]->IsRange = 0
+pp_data->cap[39]->IsAlias = 0
+pp_data->cap[39]->IsStringRange = 0
+pp_data->cap[39]->IsDesignatorRange = 0
+pp_data->cap[39]->Reserved1 = 0x000000
+pp_data->cap[39]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[39]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[39]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[39]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[39]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[39]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[39]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[39]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[39]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[39]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[39]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[39]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[39]->NotRange.Usage = 0x0002
+pp_data->cap[39]->NotRange.Reserved1 = 0x0002
+pp_data->cap[39]->NotRange.StringIndex = 0
+pp_data->cap[39]->NotRange.Reserved2 = 0
+pp_data->cap[39]->NotRange.DesignatorIndex = 0
+pp_data->cap[39]->NotRange.Reserved3 = 0
+pp_data->cap[39]->NotRange.DataIndex = 39
+pp_data->cap[39]->NotRange.Reserved4 = 39
+pp_data->cap[39]->Button.LogicalMin = 0
+pp_data->cap[39]->Button.LogicalMax = 0
+pp_data->cap[39]->Units = 0
+pp_data->cap[39]->UnitsExp = 0
+
+pp_data->cap[40]->UsagePage = 0xFF01
+pp_data->cap[40]->ReportID = 0x01
+pp_data->cap[40]->BitPosition = 3
+pp_data->cap[40]->BitSize = 1
+pp_data->cap[40]->ReportCount = 1
+pp_data->cap[40]->BytePosition = 0x0004
+pp_data->cap[40]->BitCount = 1
+pp_data->cap[40]->BitField = 0x02
+pp_data->cap[40]->NextBytePosition = 0x0005
+pp_data->cap[40]->LinkCollection = 0x0001
+pp_data->cap[40]->LinkUsagePage = 0xFF01
+pp_data->cap[40]->LinkUsage = 0x0001
+pp_data->cap[40]->IsMultipleItemsForArray = 0
+pp_data->cap[40]->IsButtonCap = 1
+pp_data->cap[40]->IsPadding = 0
+pp_data->cap[40]->IsAbsolute = 1
+pp_data->cap[40]->IsRange = 0
+pp_data->cap[40]->IsAlias = 0
+pp_data->cap[40]->IsStringRange = 0
+pp_data->cap[40]->IsDesignatorRange = 0
+pp_data->cap[40]->Reserved1 = 0x000000
+pp_data->cap[40]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[40]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[40]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[40]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[40]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[40]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[40]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[40]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[40]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[40]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[40]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[40]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[40]->NotRange.Usage = 0x0002
+pp_data->cap[40]->NotRange.Reserved1 = 0x0002
+pp_data->cap[40]->NotRange.StringIndex = 0
+pp_data->cap[40]->NotRange.Reserved2 = 0
+pp_data->cap[40]->NotRange.DesignatorIndex = 0
+pp_data->cap[40]->NotRange.Reserved3 = 0
+pp_data->cap[40]->NotRange.DataIndex = 40
+pp_data->cap[40]->NotRange.Reserved4 = 40
+pp_data->cap[40]->Button.LogicalMin = 0
+pp_data->cap[40]->Button.LogicalMax = 0
+pp_data->cap[40]->Units = 0
+pp_data->cap[40]->UnitsExp = 0
+
+pp_data->cap[41]->UsagePage = 0xFF01
+pp_data->cap[41]->ReportID = 0x01
+pp_data->cap[41]->BitPosition = 2
+pp_data->cap[41]->BitSize = 1
+pp_data->cap[41]->ReportCount = 1
+pp_data->cap[41]->BytePosition = 0x0004
+pp_data->cap[41]->BitCount = 1
+pp_data->cap[41]->BitField = 0x02
+pp_data->cap[41]->NextBytePosition = 0x0005
+pp_data->cap[41]->LinkCollection = 0x0001
+pp_data->cap[41]->LinkUsagePage = 0xFF01
+pp_data->cap[41]->LinkUsage = 0x0001
+pp_data->cap[41]->IsMultipleItemsForArray = 0
+pp_data->cap[41]->IsButtonCap = 1
+pp_data->cap[41]->IsPadding = 0
+pp_data->cap[41]->IsAbsolute = 1
+pp_data->cap[41]->IsRange = 0
+pp_data->cap[41]->IsAlias = 0
+pp_data->cap[41]->IsStringRange = 0
+pp_data->cap[41]->IsDesignatorRange = 0
+pp_data->cap[41]->Reserved1 = 0x000000
+pp_data->cap[41]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[41]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[41]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[41]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[41]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[41]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[41]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[41]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[41]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[41]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[41]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[41]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[41]->NotRange.Usage = 0x0002
+pp_data->cap[41]->NotRange.Reserved1 = 0x0002
+pp_data->cap[41]->NotRange.StringIndex = 0
+pp_data->cap[41]->NotRange.Reserved2 = 0
+pp_data->cap[41]->NotRange.DesignatorIndex = 0
+pp_data->cap[41]->NotRange.Reserved3 = 0
+pp_data->cap[41]->NotRange.DataIndex = 41
+pp_data->cap[41]->NotRange.Reserved4 = 41
+pp_data->cap[41]->Button.LogicalMin = 0
+pp_data->cap[41]->Button.LogicalMax = 0
+pp_data->cap[41]->Units = 0
+pp_data->cap[41]->UnitsExp = 0
+
+pp_data->cap[42]->UsagePage = 0xFF01
+pp_data->cap[42]->ReportID = 0x01
+pp_data->cap[42]->BitPosition = 1
+pp_data->cap[42]->BitSize = 1
+pp_data->cap[42]->ReportCount = 1
+pp_data->cap[42]->BytePosition = 0x0004
+pp_data->cap[42]->BitCount = 1
+pp_data->cap[42]->BitField = 0x02
+pp_data->cap[42]->NextBytePosition = 0x0005
+pp_data->cap[42]->LinkCollection = 0x0001
+pp_data->cap[42]->LinkUsagePage = 0xFF01
+pp_data->cap[42]->LinkUsage = 0x0001
+pp_data->cap[42]->IsMultipleItemsForArray = 0
+pp_data->cap[42]->IsButtonCap = 1
+pp_data->cap[42]->IsPadding = 0
+pp_data->cap[42]->IsAbsolute = 1
+pp_data->cap[42]->IsRange = 0
+pp_data->cap[42]->IsAlias = 0
+pp_data->cap[42]->IsStringRange = 0
+pp_data->cap[42]->IsDesignatorRange = 0
+pp_data->cap[42]->Reserved1 = 0x000000
+pp_data->cap[42]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[42]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[42]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[42]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[42]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[42]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[42]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[42]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[42]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[42]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[42]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[42]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[42]->NotRange.Usage = 0x0002
+pp_data->cap[42]->NotRange.Reserved1 = 0x0002
+pp_data->cap[42]->NotRange.StringIndex = 0
+pp_data->cap[42]->NotRange.Reserved2 = 0
+pp_data->cap[42]->NotRange.DesignatorIndex = 0
+pp_data->cap[42]->NotRange.Reserved3 = 0
+pp_data->cap[42]->NotRange.DataIndex = 42
+pp_data->cap[42]->NotRange.Reserved4 = 42
+pp_data->cap[42]->Button.LogicalMin = 0
+pp_data->cap[42]->Button.LogicalMax = 0
+pp_data->cap[42]->Units = 0
+pp_data->cap[42]->UnitsExp = 0
+
+pp_data->cap[43]->UsagePage = 0xFF01
+pp_data->cap[43]->ReportID = 0x01
+pp_data->cap[43]->BitPosition = 0
+pp_data->cap[43]->BitSize = 1
+pp_data->cap[43]->ReportCount = 1
+pp_data->cap[43]->BytePosition = 0x0004
+pp_data->cap[43]->BitCount = 1
+pp_data->cap[43]->BitField = 0x02
+pp_data->cap[43]->NextBytePosition = 0x0005
+pp_data->cap[43]->LinkCollection = 0x0001
+pp_data->cap[43]->LinkUsagePage = 0xFF01
+pp_data->cap[43]->LinkUsage = 0x0001
+pp_data->cap[43]->IsMultipleItemsForArray = 0
+pp_data->cap[43]->IsButtonCap = 1
+pp_data->cap[43]->IsPadding = 0
+pp_data->cap[43]->IsAbsolute = 1
+pp_data->cap[43]->IsRange = 0
+pp_data->cap[43]->IsAlias = 0
+pp_data->cap[43]->IsStringRange = 0
+pp_data->cap[43]->IsDesignatorRange = 0
+pp_data->cap[43]->Reserved1 = 0x000000
+pp_data->cap[43]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[43]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[43]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[43]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[43]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[43]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[43]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[43]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[43]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[43]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[43]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[43]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[43]->NotRange.Usage = 0x0002
+pp_data->cap[43]->NotRange.Reserved1 = 0x0002
+pp_data->cap[43]->NotRange.StringIndex = 0
+pp_data->cap[43]->NotRange.Reserved2 = 0
+pp_data->cap[43]->NotRange.DesignatorIndex = 0
+pp_data->cap[43]->NotRange.Reserved3 = 0
+pp_data->cap[43]->NotRange.DataIndex = 43
+pp_data->cap[43]->NotRange.Reserved4 = 43
+pp_data->cap[43]->Button.LogicalMin = 0
+pp_data->cap[43]->Button.LogicalMax = 0
+pp_data->cap[43]->Units = 0
+pp_data->cap[43]->UnitsExp = 0
+
+pp_data->cap[44]->UsagePage = 0xFF01
+pp_data->cap[44]->ReportID = 0x01
+pp_data->cap[44]->BitPosition = 7
+pp_data->cap[44]->BitSize = 1
+pp_data->cap[44]->ReportCount = 1
+pp_data->cap[44]->BytePosition = 0x0003
+pp_data->cap[44]->BitCount = 1
+pp_data->cap[44]->BitField = 0x02
+pp_data->cap[44]->NextBytePosition = 0x0004
+pp_data->cap[44]->LinkCollection = 0x0001
+pp_data->cap[44]->LinkUsagePage = 0xFF01
+pp_data->cap[44]->LinkUsage = 0x0001
+pp_data->cap[44]->IsMultipleItemsForArray = 0
+pp_data->cap[44]->IsButtonCap = 1
+pp_data->cap[44]->IsPadding = 0
+pp_data->cap[44]->IsAbsolute = 1
+pp_data->cap[44]->IsRange = 0
+pp_data->cap[44]->IsAlias = 0
+pp_data->cap[44]->IsStringRange = 0
+pp_data->cap[44]->IsDesignatorRange = 0
+pp_data->cap[44]->Reserved1 = 0x000000
+pp_data->cap[44]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[44]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[44]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[44]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[44]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[44]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[44]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[44]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[44]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[44]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[44]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[44]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[44]->NotRange.Usage = 0x0002
+pp_data->cap[44]->NotRange.Reserved1 = 0x0002
+pp_data->cap[44]->NotRange.StringIndex = 0
+pp_data->cap[44]->NotRange.Reserved2 = 0
+pp_data->cap[44]->NotRange.DesignatorIndex = 0
+pp_data->cap[44]->NotRange.Reserved3 = 0
+pp_data->cap[44]->NotRange.DataIndex = 44
+pp_data->cap[44]->NotRange.Reserved4 = 44
+pp_data->cap[44]->Button.LogicalMin = 0
+pp_data->cap[44]->Button.LogicalMax = 0
+pp_data->cap[44]->Units = 0
+pp_data->cap[44]->UnitsExp = 0
+
+pp_data->cap[45]->UsagePage = 0xFF01
+pp_data->cap[45]->ReportID = 0x01
+pp_data->cap[45]->BitPosition = 6
+pp_data->cap[45]->BitSize = 1
+pp_data->cap[45]->ReportCount = 1
+pp_data->cap[45]->BytePosition = 0x0003
+pp_data->cap[45]->BitCount = 1
+pp_data->cap[45]->BitField = 0x02
+pp_data->cap[45]->NextBytePosition = 0x0004
+pp_data->cap[45]->LinkCollection = 0x0001
+pp_data->cap[45]->LinkUsagePage = 0xFF01
+pp_data->cap[45]->LinkUsage = 0x0001
+pp_data->cap[45]->IsMultipleItemsForArray = 0
+pp_data->cap[45]->IsButtonCap = 1
+pp_data->cap[45]->IsPadding = 0
+pp_data->cap[45]->IsAbsolute = 1
+pp_data->cap[45]->IsRange = 0
+pp_data->cap[45]->IsAlias = 0
+pp_data->cap[45]->IsStringRange = 0
+pp_data->cap[45]->IsDesignatorRange = 0
+pp_data->cap[45]->Reserved1 = 0x000000
+pp_data->cap[45]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[45]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[45]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[45]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[45]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[45]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[45]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[45]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[45]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[45]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[45]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[45]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[45]->NotRange.Usage = 0x0002
+pp_data->cap[45]->NotRange.Reserved1 = 0x0002
+pp_data->cap[45]->NotRange.StringIndex = 0
+pp_data->cap[45]->NotRange.Reserved2 = 0
+pp_data->cap[45]->NotRange.DesignatorIndex = 0
+pp_data->cap[45]->NotRange.Reserved3 = 0
+pp_data->cap[45]->NotRange.DataIndex = 45
+pp_data->cap[45]->NotRange.Reserved4 = 45
+pp_data->cap[45]->Button.LogicalMin = 0
+pp_data->cap[45]->Button.LogicalMax = 0
+pp_data->cap[45]->Units = 0
+pp_data->cap[45]->UnitsExp = 0
+
+pp_data->cap[46]->UsagePage = 0xFF01
+pp_data->cap[46]->ReportID = 0x01
+pp_data->cap[46]->BitPosition = 5
+pp_data->cap[46]->BitSize = 1
+pp_data->cap[46]->ReportCount = 1
+pp_data->cap[46]->BytePosition = 0x0003
+pp_data->cap[46]->BitCount = 1
+pp_data->cap[46]->BitField = 0x02
+pp_data->cap[46]->NextBytePosition = 0x0004
+pp_data->cap[46]->LinkCollection = 0x0001
+pp_data->cap[46]->LinkUsagePage = 0xFF01
+pp_data->cap[46]->LinkUsage = 0x0001
+pp_data->cap[46]->IsMultipleItemsForArray = 0
+pp_data->cap[46]->IsButtonCap = 1
+pp_data->cap[46]->IsPadding = 0
+pp_data->cap[46]->IsAbsolute = 1
+pp_data->cap[46]->IsRange = 0
+pp_data->cap[46]->IsAlias = 0
+pp_data->cap[46]->IsStringRange = 0
+pp_data->cap[46]->IsDesignatorRange = 0
+pp_data->cap[46]->Reserved1 = 0x000000
+pp_data->cap[46]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[46]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[46]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[46]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[46]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[46]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[46]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[46]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[46]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[46]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[46]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[46]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[46]->NotRange.Usage = 0x0002
+pp_data->cap[46]->NotRange.Reserved1 = 0x0002
+pp_data->cap[46]->NotRange.StringIndex = 0
+pp_data->cap[46]->NotRange.Reserved2 = 0
+pp_data->cap[46]->NotRange.DesignatorIndex = 0
+pp_data->cap[46]->NotRange.Reserved3 = 0
+pp_data->cap[46]->NotRange.DataIndex = 46
+pp_data->cap[46]->NotRange.Reserved4 = 46
+pp_data->cap[46]->Button.LogicalMin = 0
+pp_data->cap[46]->Button.LogicalMax = 0
+pp_data->cap[46]->Units = 0
+pp_data->cap[46]->UnitsExp = 0
+
+pp_data->cap[47]->UsagePage = 0xFF01
+pp_data->cap[47]->ReportID = 0x01
+pp_data->cap[47]->BitPosition = 4
+pp_data->cap[47]->BitSize = 1
+pp_data->cap[47]->ReportCount = 1
+pp_data->cap[47]->BytePosition = 0x0003
+pp_data->cap[47]->BitCount = 1
+pp_data->cap[47]->BitField = 0x02
+pp_data->cap[47]->NextBytePosition = 0x0004
+pp_data->cap[47]->LinkCollection = 0x0001
+pp_data->cap[47]->LinkUsagePage = 0xFF01
+pp_data->cap[47]->LinkUsage = 0x0001
+pp_data->cap[47]->IsMultipleItemsForArray = 0
+pp_data->cap[47]->IsButtonCap = 1
+pp_data->cap[47]->IsPadding = 0
+pp_data->cap[47]->IsAbsolute = 1
+pp_data->cap[47]->IsRange = 0
+pp_data->cap[47]->IsAlias = 0
+pp_data->cap[47]->IsStringRange = 0
+pp_data->cap[47]->IsDesignatorRange = 0
+pp_data->cap[47]->Reserved1 = 0x000000
+pp_data->cap[47]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[47]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[47]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[47]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[47]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[47]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[47]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[47]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[47]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[47]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[47]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[47]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[47]->NotRange.Usage = 0x0002
+pp_data->cap[47]->NotRange.Reserved1 = 0x0002
+pp_data->cap[47]->NotRange.StringIndex = 0
+pp_data->cap[47]->NotRange.Reserved2 = 0
+pp_data->cap[47]->NotRange.DesignatorIndex = 0
+pp_data->cap[47]->NotRange.Reserved3 = 0
+pp_data->cap[47]->NotRange.DataIndex = 47
+pp_data->cap[47]->NotRange.Reserved4 = 47
+pp_data->cap[47]->Button.LogicalMin = 0
+pp_data->cap[47]->Button.LogicalMax = 0
+pp_data->cap[47]->Units = 0
+pp_data->cap[47]->UnitsExp = 0
+
+pp_data->cap[48]->UsagePage = 0xFF01
+pp_data->cap[48]->ReportID = 0x01
+pp_data->cap[48]->BitPosition = 3
+pp_data->cap[48]->BitSize = 1
+pp_data->cap[48]->ReportCount = 1
+pp_data->cap[48]->BytePosition = 0x0003
+pp_data->cap[48]->BitCount = 1
+pp_data->cap[48]->BitField = 0x02
+pp_data->cap[48]->NextBytePosition = 0x0004
+pp_data->cap[48]->LinkCollection = 0x0001
+pp_data->cap[48]->LinkUsagePage = 0xFF01
+pp_data->cap[48]->LinkUsage = 0x0001
+pp_data->cap[48]->IsMultipleItemsForArray = 0
+pp_data->cap[48]->IsButtonCap = 1
+pp_data->cap[48]->IsPadding = 0
+pp_data->cap[48]->IsAbsolute = 1
+pp_data->cap[48]->IsRange = 0
+pp_data->cap[48]->IsAlias = 0
+pp_data->cap[48]->IsStringRange = 0
+pp_data->cap[48]->IsDesignatorRange = 0
+pp_data->cap[48]->Reserved1 = 0x000000
+pp_data->cap[48]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[48]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[48]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[48]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[48]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[48]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[48]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[48]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[48]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[48]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[48]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[48]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[48]->NotRange.Usage = 0x0002
+pp_data->cap[48]->NotRange.Reserved1 = 0x0002
+pp_data->cap[48]->NotRange.StringIndex = 0
+pp_data->cap[48]->NotRange.Reserved2 = 0
+pp_data->cap[48]->NotRange.DesignatorIndex = 0
+pp_data->cap[48]->NotRange.Reserved3 = 0
+pp_data->cap[48]->NotRange.DataIndex = 48
+pp_data->cap[48]->NotRange.Reserved4 = 48
+pp_data->cap[48]->Button.LogicalMin = 0
+pp_data->cap[48]->Button.LogicalMax = 0
+pp_data->cap[48]->Units = 0
+pp_data->cap[48]->UnitsExp = 0
+
+pp_data->cap[49]->UsagePage = 0xFF01
+pp_data->cap[49]->ReportID = 0x01
+pp_data->cap[49]->BitPosition = 2
+pp_data->cap[49]->BitSize = 1
+pp_data->cap[49]->ReportCount = 1
+pp_data->cap[49]->BytePosition = 0x0003
+pp_data->cap[49]->BitCount = 1
+pp_data->cap[49]->BitField = 0x02
+pp_data->cap[49]->NextBytePosition = 0x0004
+pp_data->cap[49]->LinkCollection = 0x0001
+pp_data->cap[49]->LinkUsagePage = 0xFF01
+pp_data->cap[49]->LinkUsage = 0x0001
+pp_data->cap[49]->IsMultipleItemsForArray = 0
+pp_data->cap[49]->IsButtonCap = 1
+pp_data->cap[49]->IsPadding = 0
+pp_data->cap[49]->IsAbsolute = 1
+pp_data->cap[49]->IsRange = 0
+pp_data->cap[49]->IsAlias = 0
+pp_data->cap[49]->IsStringRange = 0
+pp_data->cap[49]->IsDesignatorRange = 0
+pp_data->cap[49]->Reserved1 = 0x000000
+pp_data->cap[49]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[49]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[49]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[49]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[49]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[49]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[49]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[49]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[49]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[49]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[49]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[49]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[49]->NotRange.Usage = 0x0002
+pp_data->cap[49]->NotRange.Reserved1 = 0x0002
+pp_data->cap[49]->NotRange.StringIndex = 0
+pp_data->cap[49]->NotRange.Reserved2 = 0
+pp_data->cap[49]->NotRange.DesignatorIndex = 0
+pp_data->cap[49]->NotRange.Reserved3 = 0
+pp_data->cap[49]->NotRange.DataIndex = 49
+pp_data->cap[49]->NotRange.Reserved4 = 49
+pp_data->cap[49]->Button.LogicalMin = 0
+pp_data->cap[49]->Button.LogicalMax = 0
+pp_data->cap[49]->Units = 0
+pp_data->cap[49]->UnitsExp = 0
+
+pp_data->cap[50]->UsagePage = 0xFF01
+pp_data->cap[50]->ReportID = 0x01
+pp_data->cap[50]->BitPosition = 1
+pp_data->cap[50]->BitSize = 1
+pp_data->cap[50]->ReportCount = 1
+pp_data->cap[50]->BytePosition = 0x0003
+pp_data->cap[50]->BitCount = 1
+pp_data->cap[50]->BitField = 0x02
+pp_data->cap[50]->NextBytePosition = 0x0004
+pp_data->cap[50]->LinkCollection = 0x0001
+pp_data->cap[50]->LinkUsagePage = 0xFF01
+pp_data->cap[50]->LinkUsage = 0x0001
+pp_data->cap[50]->IsMultipleItemsForArray = 0
+pp_data->cap[50]->IsButtonCap = 1
+pp_data->cap[50]->IsPadding = 0
+pp_data->cap[50]->IsAbsolute = 1
+pp_data->cap[50]->IsRange = 0
+pp_data->cap[50]->IsAlias = 0
+pp_data->cap[50]->IsStringRange = 0
+pp_data->cap[50]->IsDesignatorRange = 0
+pp_data->cap[50]->Reserved1 = 0x000000
+pp_data->cap[50]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[50]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[50]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[50]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[50]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[50]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[50]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[50]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[50]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[50]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[50]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[50]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[50]->NotRange.Usage = 0x0002
+pp_data->cap[50]->NotRange.Reserved1 = 0x0002
+pp_data->cap[50]->NotRange.StringIndex = 0
+pp_data->cap[50]->NotRange.Reserved2 = 0
+pp_data->cap[50]->NotRange.DesignatorIndex = 0
+pp_data->cap[50]->NotRange.Reserved3 = 0
+pp_data->cap[50]->NotRange.DataIndex = 50
+pp_data->cap[50]->NotRange.Reserved4 = 50
+pp_data->cap[50]->Button.LogicalMin = 0
+pp_data->cap[50]->Button.LogicalMax = 0
+pp_data->cap[50]->Units = 0
+pp_data->cap[50]->UnitsExp = 0
+
+pp_data->cap[51]->UsagePage = 0xFF01
+pp_data->cap[51]->ReportID = 0x01
+pp_data->cap[51]->BitPosition = 0
+pp_data->cap[51]->BitSize = 1
+pp_data->cap[51]->ReportCount = 1
+pp_data->cap[51]->BytePosition = 0x0003
+pp_data->cap[51]->BitCount = 1
+pp_data->cap[51]->BitField = 0x02
+pp_data->cap[51]->NextBytePosition = 0x0004
+pp_data->cap[51]->LinkCollection = 0x0001
+pp_data->cap[51]->LinkUsagePage = 0xFF01
+pp_data->cap[51]->LinkUsage = 0x0001
+pp_data->cap[51]->IsMultipleItemsForArray = 0
+pp_data->cap[51]->IsButtonCap = 1
+pp_data->cap[51]->IsPadding = 0
+pp_data->cap[51]->IsAbsolute = 1
+pp_data->cap[51]->IsRange = 0
+pp_data->cap[51]->IsAlias = 0
+pp_data->cap[51]->IsStringRange = 0
+pp_data->cap[51]->IsDesignatorRange = 0
+pp_data->cap[51]->Reserved1 = 0x000000
+pp_data->cap[51]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[51]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[51]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[51]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[51]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[51]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[51]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[51]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[51]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[51]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[51]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[51]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[51]->NotRange.Usage = 0x0002
+pp_data->cap[51]->NotRange.Reserved1 = 0x0002
+pp_data->cap[51]->NotRange.StringIndex = 0
+pp_data->cap[51]->NotRange.Reserved2 = 0
+pp_data->cap[51]->NotRange.DesignatorIndex = 0
+pp_data->cap[51]->NotRange.Reserved3 = 0
+pp_data->cap[51]->NotRange.DataIndex = 51
+pp_data->cap[51]->NotRange.Reserved4 = 51
+pp_data->cap[51]->Button.LogicalMin = 0
+pp_data->cap[51]->Button.LogicalMax = 0
+pp_data->cap[51]->Units = 0
+pp_data->cap[51]->UnitsExp = 0
+
+pp_data->cap[52]->UsagePage = 0xFF01
+pp_data->cap[52]->ReportID = 0x01
+pp_data->cap[52]->BitPosition = 7
+pp_data->cap[52]->BitSize = 1
+pp_data->cap[52]->ReportCount = 1
+pp_data->cap[52]->BytePosition = 0x0009
+pp_data->cap[52]->BitCount = 1
+pp_data->cap[52]->BitField = 0x02
+pp_data->cap[52]->NextBytePosition = 0x000A
+pp_data->cap[52]->LinkCollection = 0x0001
+pp_data->cap[52]->LinkUsagePage = 0xFF01
+pp_data->cap[52]->LinkUsage = 0x0001
+pp_data->cap[52]->IsMultipleItemsForArray = 0
+pp_data->cap[52]->IsButtonCap = 1
+pp_data->cap[52]->IsPadding = 0
+pp_data->cap[52]->IsAbsolute = 1
+pp_data->cap[52]->IsRange = 0
+pp_data->cap[52]->IsAlias = 0
+pp_data->cap[52]->IsStringRange = 0
+pp_data->cap[52]->IsDesignatorRange = 0
+pp_data->cap[52]->Reserved1 = 0x000000
+pp_data->cap[52]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[52]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[52]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[52]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[52]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[52]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[52]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[52]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[52]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[52]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[52]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[52]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[52]->NotRange.Usage = 0x000B
+pp_data->cap[52]->NotRange.Reserved1 = 0x000B
+pp_data->cap[52]->NotRange.StringIndex = 0
+pp_data->cap[52]->NotRange.Reserved2 = 0
+pp_data->cap[52]->NotRange.DesignatorIndex = 0
+pp_data->cap[52]->NotRange.Reserved3 = 0
+pp_data->cap[52]->NotRange.DataIndex = 52
+pp_data->cap[52]->NotRange.Reserved4 = 52
+pp_data->cap[52]->Button.LogicalMin = 0
+pp_data->cap[52]->Button.LogicalMax = 0
+pp_data->cap[52]->Units = 0
+pp_data->cap[52]->UnitsExp = 0
+
+pp_data->cap[53]->UsagePage = 0xFF01
+pp_data->cap[53]->ReportID = 0x01
+pp_data->cap[53]->BitPosition = 6
+pp_data->cap[53]->BitSize = 1
+pp_data->cap[53]->ReportCount = 1
+pp_data->cap[53]->BytePosition = 0x0009
+pp_data->cap[53]->BitCount = 1
+pp_data->cap[53]->BitField = 0x02
+pp_data->cap[53]->NextBytePosition = 0x000A
+pp_data->cap[53]->LinkCollection = 0x0001
+pp_data->cap[53]->LinkUsagePage = 0xFF01
+pp_data->cap[53]->LinkUsage = 0x0001
+pp_data->cap[53]->IsMultipleItemsForArray = 0
+pp_data->cap[53]->IsButtonCap = 1
+pp_data->cap[53]->IsPadding = 0
+pp_data->cap[53]->IsAbsolute = 1
+pp_data->cap[53]->IsRange = 0
+pp_data->cap[53]->IsAlias = 0
+pp_data->cap[53]->IsStringRange = 0
+pp_data->cap[53]->IsDesignatorRange = 0
+pp_data->cap[53]->Reserved1 = 0x000000
+pp_data->cap[53]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[53]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[53]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[53]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[53]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[53]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[53]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[53]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[53]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[53]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[53]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[53]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[53]->NotRange.Usage = 0x000B
+pp_data->cap[53]->NotRange.Reserved1 = 0x000B
+pp_data->cap[53]->NotRange.StringIndex = 0
+pp_data->cap[53]->NotRange.Reserved2 = 0
+pp_data->cap[53]->NotRange.DesignatorIndex = 0
+pp_data->cap[53]->NotRange.Reserved3 = 0
+pp_data->cap[53]->NotRange.DataIndex = 53
+pp_data->cap[53]->NotRange.Reserved4 = 53
+pp_data->cap[53]->Button.LogicalMin = 0
+pp_data->cap[53]->Button.LogicalMax = 0
+pp_data->cap[53]->Units = 0
+pp_data->cap[53]->UnitsExp = 0
+
+pp_data->cap[54]->UsagePage = 0xFF01
+pp_data->cap[54]->ReportID = 0x01
+pp_data->cap[54]->BitPosition = 5
+pp_data->cap[54]->BitSize = 1
+pp_data->cap[54]->ReportCount = 1
+pp_data->cap[54]->BytePosition = 0x0009
+pp_data->cap[54]->BitCount = 1
+pp_data->cap[54]->BitField = 0x02
+pp_data->cap[54]->NextBytePosition = 0x000A
+pp_data->cap[54]->LinkCollection = 0x0001
+pp_data->cap[54]->LinkUsagePage = 0xFF01
+pp_data->cap[54]->LinkUsage = 0x0001
+pp_data->cap[54]->IsMultipleItemsForArray = 0
+pp_data->cap[54]->IsButtonCap = 1
+pp_data->cap[54]->IsPadding = 0
+pp_data->cap[54]->IsAbsolute = 1
+pp_data->cap[54]->IsRange = 0
+pp_data->cap[54]->IsAlias = 0
+pp_data->cap[54]->IsStringRange = 0
+pp_data->cap[54]->IsDesignatorRange = 0
+pp_data->cap[54]->Reserved1 = 0x000000
+pp_data->cap[54]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[54]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[54]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[54]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[54]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[54]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[54]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[54]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[54]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[54]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[54]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[54]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[54]->NotRange.Usage = 0x000B
+pp_data->cap[54]->NotRange.Reserved1 = 0x000B
+pp_data->cap[54]->NotRange.StringIndex = 0
+pp_data->cap[54]->NotRange.Reserved2 = 0
+pp_data->cap[54]->NotRange.DesignatorIndex = 0
+pp_data->cap[54]->NotRange.Reserved3 = 0
+pp_data->cap[54]->NotRange.DataIndex = 54
+pp_data->cap[54]->NotRange.Reserved4 = 54
+pp_data->cap[54]->Button.LogicalMin = 0
+pp_data->cap[54]->Button.LogicalMax = 0
+pp_data->cap[54]->Units = 0
+pp_data->cap[54]->UnitsExp = 0
+
+pp_data->cap[55]->UsagePage = 0xFF01
+pp_data->cap[55]->ReportID = 0x01
+pp_data->cap[55]->BitPosition = 4
+pp_data->cap[55]->BitSize = 1
+pp_data->cap[55]->ReportCount = 1
+pp_data->cap[55]->BytePosition = 0x0009
+pp_data->cap[55]->BitCount = 1
+pp_data->cap[55]->BitField = 0x02
+pp_data->cap[55]->NextBytePosition = 0x000A
+pp_data->cap[55]->LinkCollection = 0x0001
+pp_data->cap[55]->LinkUsagePage = 0xFF01
+pp_data->cap[55]->LinkUsage = 0x0001
+pp_data->cap[55]->IsMultipleItemsForArray = 0
+pp_data->cap[55]->IsButtonCap = 1
+pp_data->cap[55]->IsPadding = 0
+pp_data->cap[55]->IsAbsolute = 1
+pp_data->cap[55]->IsRange = 0
+pp_data->cap[55]->IsAlias = 0
+pp_data->cap[55]->IsStringRange = 0
+pp_data->cap[55]->IsDesignatorRange = 0
+pp_data->cap[55]->Reserved1 = 0x000000
+pp_data->cap[55]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[55]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[55]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[55]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[55]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[55]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[55]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[55]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[55]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[55]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[55]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[55]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[55]->NotRange.Usage = 0x000B
+pp_data->cap[55]->NotRange.Reserved1 = 0x000B
+pp_data->cap[55]->NotRange.StringIndex = 0
+pp_data->cap[55]->NotRange.Reserved2 = 0
+pp_data->cap[55]->NotRange.DesignatorIndex = 0
+pp_data->cap[55]->NotRange.Reserved3 = 0
+pp_data->cap[55]->NotRange.DataIndex = 55
+pp_data->cap[55]->NotRange.Reserved4 = 55
+pp_data->cap[55]->Button.LogicalMin = 0
+pp_data->cap[55]->Button.LogicalMax = 0
+pp_data->cap[55]->Units = 0
+pp_data->cap[55]->UnitsExp = 0
+
+pp_data->cap[56]->UsagePage = 0xFF01
+pp_data->cap[56]->ReportID = 0x01
+pp_data->cap[56]->BitPosition = 3
+pp_data->cap[56]->BitSize = 1
+pp_data->cap[56]->ReportCount = 1
+pp_data->cap[56]->BytePosition = 0x0009
+pp_data->cap[56]->BitCount = 1
+pp_data->cap[56]->BitField = 0x02
+pp_data->cap[56]->NextBytePosition = 0x000A
+pp_data->cap[56]->LinkCollection = 0x0001
+pp_data->cap[56]->LinkUsagePage = 0xFF01
+pp_data->cap[56]->LinkUsage = 0x0001
+pp_data->cap[56]->IsMultipleItemsForArray = 0
+pp_data->cap[56]->IsButtonCap = 1
+pp_data->cap[56]->IsPadding = 0
+pp_data->cap[56]->IsAbsolute = 1
+pp_data->cap[56]->IsRange = 0
+pp_data->cap[56]->IsAlias = 0
+pp_data->cap[56]->IsStringRange = 0
+pp_data->cap[56]->IsDesignatorRange = 0
+pp_data->cap[56]->Reserved1 = 0x000000
+pp_data->cap[56]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[56]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[56]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[56]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[56]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[56]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[56]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[56]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[56]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[56]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[56]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[56]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[56]->NotRange.Usage = 0x000B
+pp_data->cap[56]->NotRange.Reserved1 = 0x000B
+pp_data->cap[56]->NotRange.StringIndex = 0
+pp_data->cap[56]->NotRange.Reserved2 = 0
+pp_data->cap[56]->NotRange.DesignatorIndex = 0
+pp_data->cap[56]->NotRange.Reserved3 = 0
+pp_data->cap[56]->NotRange.DataIndex = 56
+pp_data->cap[56]->NotRange.Reserved4 = 56
+pp_data->cap[56]->Button.LogicalMin = 0
+pp_data->cap[56]->Button.LogicalMax = 0
+pp_data->cap[56]->Units = 0
+pp_data->cap[56]->UnitsExp = 0
+
+pp_data->cap[57]->UsagePage = 0xFF01
+pp_data->cap[57]->ReportID = 0x01
+pp_data->cap[57]->BitPosition = 2
+pp_data->cap[57]->BitSize = 1
+pp_data->cap[57]->ReportCount = 1
+pp_data->cap[57]->BytePosition = 0x0009
+pp_data->cap[57]->BitCount = 1
+pp_data->cap[57]->BitField = 0x02
+pp_data->cap[57]->NextBytePosition = 0x000A
+pp_data->cap[57]->LinkCollection = 0x0001
+pp_data->cap[57]->LinkUsagePage = 0xFF01
+pp_data->cap[57]->LinkUsage = 0x0001
+pp_data->cap[57]->IsMultipleItemsForArray = 0
+pp_data->cap[57]->IsButtonCap = 1
+pp_data->cap[57]->IsPadding = 0
+pp_data->cap[57]->IsAbsolute = 1
+pp_data->cap[57]->IsRange = 0
+pp_data->cap[57]->IsAlias = 0
+pp_data->cap[57]->IsStringRange = 0
+pp_data->cap[57]->IsDesignatorRange = 0
+pp_data->cap[57]->Reserved1 = 0x000000
+pp_data->cap[57]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[57]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[57]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[57]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[57]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[57]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[57]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[57]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[57]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[57]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[57]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[57]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[57]->NotRange.Usage = 0x000B
+pp_data->cap[57]->NotRange.Reserved1 = 0x000B
+pp_data->cap[57]->NotRange.StringIndex = 0
+pp_data->cap[57]->NotRange.Reserved2 = 0
+pp_data->cap[57]->NotRange.DesignatorIndex = 0
+pp_data->cap[57]->NotRange.Reserved3 = 0
+pp_data->cap[57]->NotRange.DataIndex = 57
+pp_data->cap[57]->NotRange.Reserved4 = 57
+pp_data->cap[57]->Button.LogicalMin = 0
+pp_data->cap[57]->Button.LogicalMax = 0
+pp_data->cap[57]->Units = 0
+pp_data->cap[57]->UnitsExp = 0
+
+pp_data->cap[58]->UsagePage = 0xFF01
+pp_data->cap[58]->ReportID = 0x01
+pp_data->cap[58]->BitPosition = 1
+pp_data->cap[58]->BitSize = 1
+pp_data->cap[58]->ReportCount = 1
+pp_data->cap[58]->BytePosition = 0x0009
+pp_data->cap[58]->BitCount = 1
+pp_data->cap[58]->BitField = 0x02
+pp_data->cap[58]->NextBytePosition = 0x000A
+pp_data->cap[58]->LinkCollection = 0x0001
+pp_data->cap[58]->LinkUsagePage = 0xFF01
+pp_data->cap[58]->LinkUsage = 0x0001
+pp_data->cap[58]->IsMultipleItemsForArray = 0
+pp_data->cap[58]->IsButtonCap = 1
+pp_data->cap[58]->IsPadding = 0
+pp_data->cap[58]->IsAbsolute = 1
+pp_data->cap[58]->IsRange = 0
+pp_data->cap[58]->IsAlias = 0
+pp_data->cap[58]->IsStringRange = 0
+pp_data->cap[58]->IsDesignatorRange = 0
+pp_data->cap[58]->Reserved1 = 0x000000
+pp_data->cap[58]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[58]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[58]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[58]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[58]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[58]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[58]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[58]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[58]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[58]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[58]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[58]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[58]->NotRange.Usage = 0x000B
+pp_data->cap[58]->NotRange.Reserved1 = 0x000B
+pp_data->cap[58]->NotRange.StringIndex = 0
+pp_data->cap[58]->NotRange.Reserved2 = 0
+pp_data->cap[58]->NotRange.DesignatorIndex = 0
+pp_data->cap[58]->NotRange.Reserved3 = 0
+pp_data->cap[58]->NotRange.DataIndex = 58
+pp_data->cap[58]->NotRange.Reserved4 = 58
+pp_data->cap[58]->Button.LogicalMin = 0
+pp_data->cap[58]->Button.LogicalMax = 0
+pp_data->cap[58]->Units = 0
+pp_data->cap[58]->UnitsExp = 0
+
+pp_data->cap[59]->UsagePage = 0xFF01
+pp_data->cap[59]->ReportID = 0x01
+pp_data->cap[59]->BitPosition = 0
+pp_data->cap[59]->BitSize = 1
+pp_data->cap[59]->ReportCount = 1
+pp_data->cap[59]->BytePosition = 0x0009
+pp_data->cap[59]->BitCount = 1
+pp_data->cap[59]->BitField = 0x02
+pp_data->cap[59]->NextBytePosition = 0x000A
+pp_data->cap[59]->LinkCollection = 0x0001
+pp_data->cap[59]->LinkUsagePage = 0xFF01
+pp_data->cap[59]->LinkUsage = 0x0001
+pp_data->cap[59]->IsMultipleItemsForArray = 0
+pp_data->cap[59]->IsButtonCap = 1
+pp_data->cap[59]->IsPadding = 0
+pp_data->cap[59]->IsAbsolute = 1
+pp_data->cap[59]->IsRange = 0
+pp_data->cap[59]->IsAlias = 0
+pp_data->cap[59]->IsStringRange = 0
+pp_data->cap[59]->IsDesignatorRange = 0
+pp_data->cap[59]->Reserved1 = 0x000000
+pp_data->cap[59]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[59]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[59]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[59]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[59]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[59]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[59]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[59]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[59]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[59]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[59]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[59]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[59]->NotRange.Usage = 0x000B
+pp_data->cap[59]->NotRange.Reserved1 = 0x000B
+pp_data->cap[59]->NotRange.StringIndex = 0
+pp_data->cap[59]->NotRange.Reserved2 = 0
+pp_data->cap[59]->NotRange.DesignatorIndex = 0
+pp_data->cap[59]->NotRange.Reserved3 = 0
+pp_data->cap[59]->NotRange.DataIndex = 59
+pp_data->cap[59]->NotRange.Reserved4 = 59
+pp_data->cap[59]->Button.LogicalMin = 0
+pp_data->cap[59]->Button.LogicalMax = 0
+pp_data->cap[59]->Units = 0
+pp_data->cap[59]->UnitsExp = 0
+
+pp_data->cap[60]->UsagePage = 0xFF01
+pp_data->cap[60]->ReportID = 0x02
+pp_data->cap[60]->BitPosition = 0
+pp_data->cap[60]->BitSize = 16
+pp_data->cap[60]->ReportCount = 1
+pp_data->cap[60]->BytePosition = 0x0033
+pp_data->cap[60]->BitCount = 16
+pp_data->cap[60]->BitField = 0x02
+pp_data->cap[60]->NextBytePosition = 0x0035
+pp_data->cap[60]->LinkCollection = 0x0002
+pp_data->cap[60]->LinkUsagePage = 0xFF01
+pp_data->cap[60]->LinkUsage = 0x0002
+pp_data->cap[60]->IsMultipleItemsForArray = 0
+pp_data->cap[60]->IsButtonCap = 0
+pp_data->cap[60]->IsPadding = 0
+pp_data->cap[60]->IsAbsolute = 1
+pp_data->cap[60]->IsRange = 0
+pp_data->cap[60]->IsAlias = 0
+pp_data->cap[60]->IsStringRange = 0
+pp_data->cap[60]->IsDesignatorRange = 0
+pp_data->cap[60]->Reserved1 = 0x000000
+pp_data->cap[60]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[60]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[60]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[60]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[60]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[60]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[60]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[60]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[60]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[60]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[60]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[60]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[60]->NotRange.Usage = 0x0004
+pp_data->cap[60]->NotRange.Reserved1 = 0x0004
+pp_data->cap[60]->NotRange.StringIndex = 0
+pp_data->cap[60]->NotRange.Reserved2 = 0
+pp_data->cap[60]->NotRange.DesignatorIndex = 0
+pp_data->cap[60]->NotRange.Reserved3 = 0
+pp_data->cap[60]->NotRange.DataIndex = 60
+pp_data->cap[60]->NotRange.Reserved4 = 60
+pp_data->cap[60]->NotButton.HasNull = 0
+pp_data->cap[60]->NotButton.Reserved4 = 0x000000
+pp_data->cap[60]->NotButton.LogicalMin = 0
+pp_data->cap[60]->NotButton.LogicalMax = 4095
+pp_data->cap[60]->NotButton.PhysicalMin = 0
+pp_data->cap[60]->NotButton.PhysicalMax = 0
+pp_data->cap[60]->Units = 0
+pp_data->cap[60]->UnitsExp = 0
+
+pp_data->cap[61]->UsagePage = 0xFF01
+pp_data->cap[61]->ReportID = 0x02
+pp_data->cap[61]->BitPosition = 0
+pp_data->cap[61]->BitSize = 16
+pp_data->cap[61]->ReportCount = 1
+pp_data->cap[61]->BytePosition = 0x0031
+pp_data->cap[61]->BitCount = 16
+pp_data->cap[61]->BitField = 0x02
+pp_data->cap[61]->NextBytePosition = 0x0033
+pp_data->cap[61]->LinkCollection = 0x0002
+pp_data->cap[61]->LinkUsagePage = 0xFF01
+pp_data->cap[61]->LinkUsage = 0x0002
+pp_data->cap[61]->IsMultipleItemsForArray = 0
+pp_data->cap[61]->IsButtonCap = 0
+pp_data->cap[61]->IsPadding = 0
+pp_data->cap[61]->IsAbsolute = 1
+pp_data->cap[61]->IsRange = 0
+pp_data->cap[61]->IsAlias = 0
+pp_data->cap[61]->IsStringRange = 0
+pp_data->cap[61]->IsDesignatorRange = 0
+pp_data->cap[61]->Reserved1 = 0x000000
+pp_data->cap[61]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[61]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[61]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[61]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[61]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[61]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[61]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[61]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[61]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[61]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[61]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[61]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[61]->NotRange.Usage = 0x0004
+pp_data->cap[61]->NotRange.Reserved1 = 0x0004
+pp_data->cap[61]->NotRange.StringIndex = 0
+pp_data->cap[61]->NotRange.Reserved2 = 0
+pp_data->cap[61]->NotRange.DesignatorIndex = 0
+pp_data->cap[61]->NotRange.Reserved3 = 0
+pp_data->cap[61]->NotRange.DataIndex = 61
+pp_data->cap[61]->NotRange.Reserved4 = 61
+pp_data->cap[61]->NotButton.HasNull = 0
+pp_data->cap[61]->NotButton.Reserved4 = 0x000000
+pp_data->cap[61]->NotButton.LogicalMin = 0
+pp_data->cap[61]->NotButton.LogicalMax = 4095
+pp_data->cap[61]->NotButton.PhysicalMin = 0
+pp_data->cap[61]->NotButton.PhysicalMax = 0
+pp_data->cap[61]->Units = 0
+pp_data->cap[61]->UnitsExp = 0
+
+pp_data->cap[62]->UsagePage = 0xFF01
+pp_data->cap[62]->ReportID = 0x02
+pp_data->cap[62]->BitPosition = 0
+pp_data->cap[62]->BitSize = 16
+pp_data->cap[62]->ReportCount = 1
+pp_data->cap[62]->BytePosition = 0x002F
+pp_data->cap[62]->BitCount = 16
+pp_data->cap[62]->BitField = 0x02
+pp_data->cap[62]->NextBytePosition = 0x0031
+pp_data->cap[62]->LinkCollection = 0x0002
+pp_data->cap[62]->LinkUsagePage = 0xFF01
+pp_data->cap[62]->LinkUsage = 0x0002
+pp_data->cap[62]->IsMultipleItemsForArray = 0
+pp_data->cap[62]->IsButtonCap = 0
+pp_data->cap[62]->IsPadding = 0
+pp_data->cap[62]->IsAbsolute = 1
+pp_data->cap[62]->IsRange = 0
+pp_data->cap[62]->IsAlias = 0
+pp_data->cap[62]->IsStringRange = 0
+pp_data->cap[62]->IsDesignatorRange = 0
+pp_data->cap[62]->Reserved1 = 0x000000
+pp_data->cap[62]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[62]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[62]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[62]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[62]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[62]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[62]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[62]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[62]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[62]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[62]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[62]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[62]->NotRange.Usage = 0x0004
+pp_data->cap[62]->NotRange.Reserved1 = 0x0004
+pp_data->cap[62]->NotRange.StringIndex = 0
+pp_data->cap[62]->NotRange.Reserved2 = 0
+pp_data->cap[62]->NotRange.DesignatorIndex = 0
+pp_data->cap[62]->NotRange.Reserved3 = 0
+pp_data->cap[62]->NotRange.DataIndex = 62
+pp_data->cap[62]->NotRange.Reserved4 = 62
+pp_data->cap[62]->NotButton.HasNull = 0
+pp_data->cap[62]->NotButton.Reserved4 = 0x000000
+pp_data->cap[62]->NotButton.LogicalMin = 0
+pp_data->cap[62]->NotButton.LogicalMax = 4095
+pp_data->cap[62]->NotButton.PhysicalMin = 0
+pp_data->cap[62]->NotButton.PhysicalMax = 0
+pp_data->cap[62]->Units = 0
+pp_data->cap[62]->UnitsExp = 0
+
+pp_data->cap[63]->UsagePage = 0xFF01
+pp_data->cap[63]->ReportID = 0x02
+pp_data->cap[63]->BitPosition = 0
+pp_data->cap[63]->BitSize = 16
+pp_data->cap[63]->ReportCount = 1
+pp_data->cap[63]->BytePosition = 0x002D
+pp_data->cap[63]->BitCount = 16
+pp_data->cap[63]->BitField = 0x02
+pp_data->cap[63]->NextBytePosition = 0x002F
+pp_data->cap[63]->LinkCollection = 0x0002
+pp_data->cap[63]->LinkUsagePage = 0xFF01
+pp_data->cap[63]->LinkUsage = 0x0002
+pp_data->cap[63]->IsMultipleItemsForArray = 0
+pp_data->cap[63]->IsButtonCap = 0
+pp_data->cap[63]->IsPadding = 0
+pp_data->cap[63]->IsAbsolute = 1
+pp_data->cap[63]->IsRange = 0
+pp_data->cap[63]->IsAlias = 0
+pp_data->cap[63]->IsStringRange = 0
+pp_data->cap[63]->IsDesignatorRange = 0
+pp_data->cap[63]->Reserved1 = 0x000000
+pp_data->cap[63]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[63]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[63]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[63]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[63]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[63]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[63]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[63]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[63]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[63]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[63]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[63]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[63]->NotRange.Usage = 0x0004
+pp_data->cap[63]->NotRange.Reserved1 = 0x0004
+pp_data->cap[63]->NotRange.StringIndex = 0
+pp_data->cap[63]->NotRange.Reserved2 = 0
+pp_data->cap[63]->NotRange.DesignatorIndex = 0
+pp_data->cap[63]->NotRange.Reserved3 = 0
+pp_data->cap[63]->NotRange.DataIndex = 63
+pp_data->cap[63]->NotRange.Reserved4 = 63
+pp_data->cap[63]->NotButton.HasNull = 0
+pp_data->cap[63]->NotButton.Reserved4 = 0x000000
+pp_data->cap[63]->NotButton.LogicalMin = 0
+pp_data->cap[63]->NotButton.LogicalMax = 4095
+pp_data->cap[63]->NotButton.PhysicalMin = 0
+pp_data->cap[63]->NotButton.PhysicalMax = 0
+pp_data->cap[63]->Units = 0
+pp_data->cap[63]->UnitsExp = 0
+
+pp_data->cap[64]->UsagePage = 0xFF01
+pp_data->cap[64]->ReportID = 0x02
+pp_data->cap[64]->BitPosition = 0
+pp_data->cap[64]->BitSize = 16
+pp_data->cap[64]->ReportCount = 1
+pp_data->cap[64]->BytePosition = 0x002B
+pp_data->cap[64]->BitCount = 16
+pp_data->cap[64]->BitField = 0x02
+pp_data->cap[64]->NextBytePosition = 0x002D
+pp_data->cap[64]->LinkCollection = 0x0002
+pp_data->cap[64]->LinkUsagePage = 0xFF01
+pp_data->cap[64]->LinkUsage = 0x0002
+pp_data->cap[64]->IsMultipleItemsForArray = 0
+pp_data->cap[64]->IsButtonCap = 0
+pp_data->cap[64]->IsPadding = 0
+pp_data->cap[64]->IsAbsolute = 1
+pp_data->cap[64]->IsRange = 0
+pp_data->cap[64]->IsAlias = 0
+pp_data->cap[64]->IsStringRange = 0
+pp_data->cap[64]->IsDesignatorRange = 0
+pp_data->cap[64]->Reserved1 = 0x000000
+pp_data->cap[64]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[64]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[64]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[64]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[64]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[64]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[64]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[64]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[64]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[64]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[64]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[64]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[64]->NotRange.Usage = 0x0004
+pp_data->cap[64]->NotRange.Reserved1 = 0x0004
+pp_data->cap[64]->NotRange.StringIndex = 0
+pp_data->cap[64]->NotRange.Reserved2 = 0
+pp_data->cap[64]->NotRange.DesignatorIndex = 0
+pp_data->cap[64]->NotRange.Reserved3 = 0
+pp_data->cap[64]->NotRange.DataIndex = 64
+pp_data->cap[64]->NotRange.Reserved4 = 64
+pp_data->cap[64]->NotButton.HasNull = 0
+pp_data->cap[64]->NotButton.Reserved4 = 0x000000
+pp_data->cap[64]->NotButton.LogicalMin = 0
+pp_data->cap[64]->NotButton.LogicalMax = 4095
+pp_data->cap[64]->NotButton.PhysicalMin = 0
+pp_data->cap[64]->NotButton.PhysicalMax = 0
+pp_data->cap[64]->Units = 0
+pp_data->cap[64]->UnitsExp = 0
+
+pp_data->cap[65]->UsagePage = 0xFF01
+pp_data->cap[65]->ReportID = 0x02
+pp_data->cap[65]->BitPosition = 0
+pp_data->cap[65]->BitSize = 16
+pp_data->cap[65]->ReportCount = 1
+pp_data->cap[65]->BytePosition = 0x0029
+pp_data->cap[65]->BitCount = 16
+pp_data->cap[65]->BitField = 0x02
+pp_data->cap[65]->NextBytePosition = 0x002B
+pp_data->cap[65]->LinkCollection = 0x0002
+pp_data->cap[65]->LinkUsagePage = 0xFF01
+pp_data->cap[65]->LinkUsage = 0x0002
+pp_data->cap[65]->IsMultipleItemsForArray = 0
+pp_data->cap[65]->IsButtonCap = 0
+pp_data->cap[65]->IsPadding = 0
+pp_data->cap[65]->IsAbsolute = 1
+pp_data->cap[65]->IsRange = 0
+pp_data->cap[65]->IsAlias = 0
+pp_data->cap[65]->IsStringRange = 0
+pp_data->cap[65]->IsDesignatorRange = 0
+pp_data->cap[65]->Reserved1 = 0x000000
+pp_data->cap[65]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[65]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[65]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[65]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[65]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[65]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[65]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[65]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[65]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[65]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[65]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[65]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[65]->NotRange.Usage = 0x0004
+pp_data->cap[65]->NotRange.Reserved1 = 0x0004
+pp_data->cap[65]->NotRange.StringIndex = 0
+pp_data->cap[65]->NotRange.Reserved2 = 0
+pp_data->cap[65]->NotRange.DesignatorIndex = 0
+pp_data->cap[65]->NotRange.Reserved3 = 0
+pp_data->cap[65]->NotRange.DataIndex = 65
+pp_data->cap[65]->NotRange.Reserved4 = 65
+pp_data->cap[65]->NotButton.HasNull = 0
+pp_data->cap[65]->NotButton.Reserved4 = 0x000000
+pp_data->cap[65]->NotButton.LogicalMin = 0
+pp_data->cap[65]->NotButton.LogicalMax = 4095
+pp_data->cap[65]->NotButton.PhysicalMin = 0
+pp_data->cap[65]->NotButton.PhysicalMax = 0
+pp_data->cap[65]->Units = 0
+pp_data->cap[65]->UnitsExp = 0
+
+pp_data->cap[66]->UsagePage = 0xFF01
+pp_data->cap[66]->ReportID = 0x02
+pp_data->cap[66]->BitPosition = 0
+pp_data->cap[66]->BitSize = 16
+pp_data->cap[66]->ReportCount = 1
+pp_data->cap[66]->BytePosition = 0x0027
+pp_data->cap[66]->BitCount = 16
+pp_data->cap[66]->BitField = 0x02
+pp_data->cap[66]->NextBytePosition = 0x0029
+pp_data->cap[66]->LinkCollection = 0x0002
+pp_data->cap[66]->LinkUsagePage = 0xFF01
+pp_data->cap[66]->LinkUsage = 0x0002
+pp_data->cap[66]->IsMultipleItemsForArray = 0
+pp_data->cap[66]->IsButtonCap = 0
+pp_data->cap[66]->IsPadding = 0
+pp_data->cap[66]->IsAbsolute = 1
+pp_data->cap[66]->IsRange = 0
+pp_data->cap[66]->IsAlias = 0
+pp_data->cap[66]->IsStringRange = 0
+pp_data->cap[66]->IsDesignatorRange = 0
+pp_data->cap[66]->Reserved1 = 0x000000
+pp_data->cap[66]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[66]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[66]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[66]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[66]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[66]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[66]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[66]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[66]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[66]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[66]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[66]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[66]->NotRange.Usage = 0x0004
+pp_data->cap[66]->NotRange.Reserved1 = 0x0004
+pp_data->cap[66]->NotRange.StringIndex = 0
+pp_data->cap[66]->NotRange.Reserved2 = 0
+pp_data->cap[66]->NotRange.DesignatorIndex = 0
+pp_data->cap[66]->NotRange.Reserved3 = 0
+pp_data->cap[66]->NotRange.DataIndex = 66
+pp_data->cap[66]->NotRange.Reserved4 = 66
+pp_data->cap[66]->NotButton.HasNull = 0
+pp_data->cap[66]->NotButton.Reserved4 = 0x000000
+pp_data->cap[66]->NotButton.LogicalMin = 0
+pp_data->cap[66]->NotButton.LogicalMax = 4095
+pp_data->cap[66]->NotButton.PhysicalMin = 0
+pp_data->cap[66]->NotButton.PhysicalMax = 0
+pp_data->cap[66]->Units = 0
+pp_data->cap[66]->UnitsExp = 0
+
+pp_data->cap[67]->UsagePage = 0xFF01
+pp_data->cap[67]->ReportID = 0x02
+pp_data->cap[67]->BitPosition = 0
+pp_data->cap[67]->BitSize = 16
+pp_data->cap[67]->ReportCount = 1
+pp_data->cap[67]->BytePosition = 0x0025
+pp_data->cap[67]->BitCount = 16
+pp_data->cap[67]->BitField = 0x02
+pp_data->cap[67]->NextBytePosition = 0x0027
+pp_data->cap[67]->LinkCollection = 0x0002
+pp_data->cap[67]->LinkUsagePage = 0xFF01
+pp_data->cap[67]->LinkUsage = 0x0002
+pp_data->cap[67]->IsMultipleItemsForArray = 0
+pp_data->cap[67]->IsButtonCap = 0
+pp_data->cap[67]->IsPadding = 0
+pp_data->cap[67]->IsAbsolute = 1
+pp_data->cap[67]->IsRange = 0
+pp_data->cap[67]->IsAlias = 0
+pp_data->cap[67]->IsStringRange = 0
+pp_data->cap[67]->IsDesignatorRange = 0
+pp_data->cap[67]->Reserved1 = 0x000000
+pp_data->cap[67]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[67]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[67]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[67]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[67]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[67]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[67]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[67]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[67]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[67]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[67]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[67]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[67]->NotRange.Usage = 0x0004
+pp_data->cap[67]->NotRange.Reserved1 = 0x0004
+pp_data->cap[67]->NotRange.StringIndex = 0
+pp_data->cap[67]->NotRange.Reserved2 = 0
+pp_data->cap[67]->NotRange.DesignatorIndex = 0
+pp_data->cap[67]->NotRange.Reserved3 = 0
+pp_data->cap[67]->NotRange.DataIndex = 67
+pp_data->cap[67]->NotRange.Reserved4 = 67
+pp_data->cap[67]->NotButton.HasNull = 0
+pp_data->cap[67]->NotButton.Reserved4 = 0x000000
+pp_data->cap[67]->NotButton.LogicalMin = 0
+pp_data->cap[67]->NotButton.LogicalMax = 4095
+pp_data->cap[67]->NotButton.PhysicalMin = 0
+pp_data->cap[67]->NotButton.PhysicalMax = 0
+pp_data->cap[67]->Units = 0
+pp_data->cap[67]->UnitsExp = 0
+
+pp_data->cap[68]->UsagePage = 0xFF01
+pp_data->cap[68]->ReportID = 0x02
+pp_data->cap[68]->BitPosition = 0
+pp_data->cap[68]->BitSize = 16
+pp_data->cap[68]->ReportCount = 1
+pp_data->cap[68]->BytePosition = 0x0023
+pp_data->cap[68]->BitCount = 16
+pp_data->cap[68]->BitField = 0x02
+pp_data->cap[68]->NextBytePosition = 0x0025
+pp_data->cap[68]->LinkCollection = 0x0002
+pp_data->cap[68]->LinkUsagePage = 0xFF01
+pp_data->cap[68]->LinkUsage = 0x0002
+pp_data->cap[68]->IsMultipleItemsForArray = 0
+pp_data->cap[68]->IsButtonCap = 0
+pp_data->cap[68]->IsPadding = 0
+pp_data->cap[68]->IsAbsolute = 1
+pp_data->cap[68]->IsRange = 0
+pp_data->cap[68]->IsAlias = 0
+pp_data->cap[68]->IsStringRange = 0
+pp_data->cap[68]->IsDesignatorRange = 0
+pp_data->cap[68]->Reserved1 = 0x000000
+pp_data->cap[68]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[68]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[68]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[68]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[68]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[68]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[68]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[68]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[68]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[68]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[68]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[68]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[68]->NotRange.Usage = 0x0004
+pp_data->cap[68]->NotRange.Reserved1 = 0x0004
+pp_data->cap[68]->NotRange.StringIndex = 0
+pp_data->cap[68]->NotRange.Reserved2 = 0
+pp_data->cap[68]->NotRange.DesignatorIndex = 0
+pp_data->cap[68]->NotRange.Reserved3 = 0
+pp_data->cap[68]->NotRange.DataIndex = 68
+pp_data->cap[68]->NotRange.Reserved4 = 68
+pp_data->cap[68]->NotButton.HasNull = 0
+pp_data->cap[68]->NotButton.Reserved4 = 0x000000
+pp_data->cap[68]->NotButton.LogicalMin = 0
+pp_data->cap[68]->NotButton.LogicalMax = 4095
+pp_data->cap[68]->NotButton.PhysicalMin = 0
+pp_data->cap[68]->NotButton.PhysicalMax = 0
+pp_data->cap[68]->Units = 0
+pp_data->cap[68]->UnitsExp = 0
+
+pp_data->cap[69]->UsagePage = 0xFF01
+pp_data->cap[69]->ReportID = 0x02
+pp_data->cap[69]->BitPosition = 0
+pp_data->cap[69]->BitSize = 16
+pp_data->cap[69]->ReportCount = 1
+pp_data->cap[69]->BytePosition = 0x0021
+pp_data->cap[69]->BitCount = 16
+pp_data->cap[69]->BitField = 0x02
+pp_data->cap[69]->NextBytePosition = 0x0023
+pp_data->cap[69]->LinkCollection = 0x0002
+pp_data->cap[69]->LinkUsagePage = 0xFF01
+pp_data->cap[69]->LinkUsage = 0x0002
+pp_data->cap[69]->IsMultipleItemsForArray = 0
+pp_data->cap[69]->IsButtonCap = 0
+pp_data->cap[69]->IsPadding = 0
+pp_data->cap[69]->IsAbsolute = 1
+pp_data->cap[69]->IsRange = 0
+pp_data->cap[69]->IsAlias = 0
+pp_data->cap[69]->IsStringRange = 0
+pp_data->cap[69]->IsDesignatorRange = 0
+pp_data->cap[69]->Reserved1 = 0x000000
+pp_data->cap[69]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[69]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[69]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[69]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[69]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[69]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[69]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[69]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[69]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[69]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[69]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[69]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[69]->NotRange.Usage = 0x0004
+pp_data->cap[69]->NotRange.Reserved1 = 0x0004
+pp_data->cap[69]->NotRange.StringIndex = 0
+pp_data->cap[69]->NotRange.Reserved2 = 0
+pp_data->cap[69]->NotRange.DesignatorIndex = 0
+pp_data->cap[69]->NotRange.Reserved3 = 0
+pp_data->cap[69]->NotRange.DataIndex = 69
+pp_data->cap[69]->NotRange.Reserved4 = 69
+pp_data->cap[69]->NotButton.HasNull = 0
+pp_data->cap[69]->NotButton.Reserved4 = 0x000000
+pp_data->cap[69]->NotButton.LogicalMin = 0
+pp_data->cap[69]->NotButton.LogicalMax = 4095
+pp_data->cap[69]->NotButton.PhysicalMin = 0
+pp_data->cap[69]->NotButton.PhysicalMax = 0
+pp_data->cap[69]->Units = 0
+pp_data->cap[69]->UnitsExp = 0
+
+pp_data->cap[70]->UsagePage = 0xFF01
+pp_data->cap[70]->ReportID = 0x02
+pp_data->cap[70]->BitPosition = 0
+pp_data->cap[70]->BitSize = 16
+pp_data->cap[70]->ReportCount = 1
+pp_data->cap[70]->BytePosition = 0x001F
+pp_data->cap[70]->BitCount = 16
+pp_data->cap[70]->BitField = 0x02
+pp_data->cap[70]->NextBytePosition = 0x0021
+pp_data->cap[70]->LinkCollection = 0x0002
+pp_data->cap[70]->LinkUsagePage = 0xFF01
+pp_data->cap[70]->LinkUsage = 0x0002
+pp_data->cap[70]->IsMultipleItemsForArray = 0
+pp_data->cap[70]->IsButtonCap = 0
+pp_data->cap[70]->IsPadding = 0
+pp_data->cap[70]->IsAbsolute = 1
+pp_data->cap[70]->IsRange = 0
+pp_data->cap[70]->IsAlias = 0
+pp_data->cap[70]->IsStringRange = 0
+pp_data->cap[70]->IsDesignatorRange = 0
+pp_data->cap[70]->Reserved1 = 0x000000
+pp_data->cap[70]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[70]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[70]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[70]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[70]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[70]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[70]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[70]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[70]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[70]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[70]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[70]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[70]->NotRange.Usage = 0x0004
+pp_data->cap[70]->NotRange.Reserved1 = 0x0004
+pp_data->cap[70]->NotRange.StringIndex = 0
+pp_data->cap[70]->NotRange.Reserved2 = 0
+pp_data->cap[70]->NotRange.DesignatorIndex = 0
+pp_data->cap[70]->NotRange.Reserved3 = 0
+pp_data->cap[70]->NotRange.DataIndex = 70
+pp_data->cap[70]->NotRange.Reserved4 = 70
+pp_data->cap[70]->NotButton.HasNull = 0
+pp_data->cap[70]->NotButton.Reserved4 = 0x000000
+pp_data->cap[70]->NotButton.LogicalMin = 0
+pp_data->cap[70]->NotButton.LogicalMax = 4095
+pp_data->cap[70]->NotButton.PhysicalMin = 0
+pp_data->cap[70]->NotButton.PhysicalMax = 0
+pp_data->cap[70]->Units = 0
+pp_data->cap[70]->UnitsExp = 0
+
+pp_data->cap[71]->UsagePage = 0xFF01
+pp_data->cap[71]->ReportID = 0x02
+pp_data->cap[71]->BitPosition = 0
+pp_data->cap[71]->BitSize = 16
+pp_data->cap[71]->ReportCount = 1
+pp_data->cap[71]->BytePosition = 0x001D
+pp_data->cap[71]->BitCount = 16
+pp_data->cap[71]->BitField = 0x02
+pp_data->cap[71]->NextBytePosition = 0x001F
+pp_data->cap[71]->LinkCollection = 0x0002
+pp_data->cap[71]->LinkUsagePage = 0xFF01
+pp_data->cap[71]->LinkUsage = 0x0002
+pp_data->cap[71]->IsMultipleItemsForArray = 0
+pp_data->cap[71]->IsButtonCap = 0
+pp_data->cap[71]->IsPadding = 0
+pp_data->cap[71]->IsAbsolute = 1
+pp_data->cap[71]->IsRange = 0
+pp_data->cap[71]->IsAlias = 0
+pp_data->cap[71]->IsStringRange = 0
+pp_data->cap[71]->IsDesignatorRange = 0
+pp_data->cap[71]->Reserved1 = 0x000000
+pp_data->cap[71]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[71]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[71]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[71]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[71]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[71]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[71]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[71]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[71]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[71]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[71]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[71]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[71]->NotRange.Usage = 0x0004
+pp_data->cap[71]->NotRange.Reserved1 = 0x0004
+pp_data->cap[71]->NotRange.StringIndex = 0
+pp_data->cap[71]->NotRange.Reserved2 = 0
+pp_data->cap[71]->NotRange.DesignatorIndex = 0
+pp_data->cap[71]->NotRange.Reserved3 = 0
+pp_data->cap[71]->NotRange.DataIndex = 71
+pp_data->cap[71]->NotRange.Reserved4 = 71
+pp_data->cap[71]->NotButton.HasNull = 0
+pp_data->cap[71]->NotButton.Reserved4 = 0x000000
+pp_data->cap[71]->NotButton.LogicalMin = 0
+pp_data->cap[71]->NotButton.LogicalMax = 4095
+pp_data->cap[71]->NotButton.PhysicalMin = 0
+pp_data->cap[71]->NotButton.PhysicalMax = 0
+pp_data->cap[71]->Units = 0
+pp_data->cap[71]->UnitsExp = 0
+
+pp_data->cap[72]->UsagePage = 0xFF01
+pp_data->cap[72]->ReportID = 0x02
+pp_data->cap[72]->BitPosition = 0
+pp_data->cap[72]->BitSize = 16
+pp_data->cap[72]->ReportCount = 1
+pp_data->cap[72]->BytePosition = 0x001B
+pp_data->cap[72]->BitCount = 16
+pp_data->cap[72]->BitField = 0x02
+pp_data->cap[72]->NextBytePosition = 0x001D
+pp_data->cap[72]->LinkCollection = 0x0002
+pp_data->cap[72]->LinkUsagePage = 0xFF01
+pp_data->cap[72]->LinkUsage = 0x0002
+pp_data->cap[72]->IsMultipleItemsForArray = 0
+pp_data->cap[72]->IsButtonCap = 0
+pp_data->cap[72]->IsPadding = 0
+pp_data->cap[72]->IsAbsolute = 1
+pp_data->cap[72]->IsRange = 0
+pp_data->cap[72]->IsAlias = 0
+pp_data->cap[72]->IsStringRange = 0
+pp_data->cap[72]->IsDesignatorRange = 0
+pp_data->cap[72]->Reserved1 = 0x000000
+pp_data->cap[72]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[72]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[72]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[72]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[72]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[72]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[72]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[72]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[72]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[72]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[72]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[72]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[72]->NotRange.Usage = 0x0004
+pp_data->cap[72]->NotRange.Reserved1 = 0x0004
+pp_data->cap[72]->NotRange.StringIndex = 0
+pp_data->cap[72]->NotRange.Reserved2 = 0
+pp_data->cap[72]->NotRange.DesignatorIndex = 0
+pp_data->cap[72]->NotRange.Reserved3 = 0
+pp_data->cap[72]->NotRange.DataIndex = 72
+pp_data->cap[72]->NotRange.Reserved4 = 72
+pp_data->cap[72]->NotButton.HasNull = 0
+pp_data->cap[72]->NotButton.Reserved4 = 0x000000
+pp_data->cap[72]->NotButton.LogicalMin = 0
+pp_data->cap[72]->NotButton.LogicalMax = 4095
+pp_data->cap[72]->NotButton.PhysicalMin = 0
+pp_data->cap[72]->NotButton.PhysicalMax = 0
+pp_data->cap[72]->Units = 0
+pp_data->cap[72]->UnitsExp = 0
+
+pp_data->cap[73]->UsagePage = 0xFF01
+pp_data->cap[73]->ReportID = 0x02
+pp_data->cap[73]->BitPosition = 0
+pp_data->cap[73]->BitSize = 16
+pp_data->cap[73]->ReportCount = 1
+pp_data->cap[73]->BytePosition = 0x0019
+pp_data->cap[73]->BitCount = 16
+pp_data->cap[73]->BitField = 0x02
+pp_data->cap[73]->NextBytePosition = 0x001B
+pp_data->cap[73]->LinkCollection = 0x0002
+pp_data->cap[73]->LinkUsagePage = 0xFF01
+pp_data->cap[73]->LinkUsage = 0x0002
+pp_data->cap[73]->IsMultipleItemsForArray = 0
+pp_data->cap[73]->IsButtonCap = 0
+pp_data->cap[73]->IsPadding = 0
+pp_data->cap[73]->IsAbsolute = 1
+pp_data->cap[73]->IsRange = 0
+pp_data->cap[73]->IsAlias = 0
+pp_data->cap[73]->IsStringRange = 0
+pp_data->cap[73]->IsDesignatorRange = 0
+pp_data->cap[73]->Reserved1 = 0x000000
+pp_data->cap[73]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[73]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[73]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[73]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[73]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[73]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[73]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[73]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[73]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[73]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[73]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[73]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[73]->NotRange.Usage = 0x0004
+pp_data->cap[73]->NotRange.Reserved1 = 0x0004
+pp_data->cap[73]->NotRange.StringIndex = 0
+pp_data->cap[73]->NotRange.Reserved2 = 0
+pp_data->cap[73]->NotRange.DesignatorIndex = 0
+pp_data->cap[73]->NotRange.Reserved3 = 0
+pp_data->cap[73]->NotRange.DataIndex = 73
+pp_data->cap[73]->NotRange.Reserved4 = 73
+pp_data->cap[73]->NotButton.HasNull = 0
+pp_data->cap[73]->NotButton.Reserved4 = 0x000000
+pp_data->cap[73]->NotButton.LogicalMin = 0
+pp_data->cap[73]->NotButton.LogicalMax = 4095
+pp_data->cap[73]->NotButton.PhysicalMin = 0
+pp_data->cap[73]->NotButton.PhysicalMax = 0
+pp_data->cap[73]->Units = 0
+pp_data->cap[73]->UnitsExp = 0
+
+pp_data->cap[74]->UsagePage = 0xFF01
+pp_data->cap[74]->ReportID = 0x02
+pp_data->cap[74]->BitPosition = 0
+pp_data->cap[74]->BitSize = 16
+pp_data->cap[74]->ReportCount = 1
+pp_data->cap[74]->BytePosition = 0x0017
+pp_data->cap[74]->BitCount = 16
+pp_data->cap[74]->BitField = 0x02
+pp_data->cap[74]->NextBytePosition = 0x0019
+pp_data->cap[74]->LinkCollection = 0x0002
+pp_data->cap[74]->LinkUsagePage = 0xFF01
+pp_data->cap[74]->LinkUsage = 0x0002
+pp_data->cap[74]->IsMultipleItemsForArray = 0
+pp_data->cap[74]->IsButtonCap = 0
+pp_data->cap[74]->IsPadding = 0
+pp_data->cap[74]->IsAbsolute = 1
+pp_data->cap[74]->IsRange = 0
+pp_data->cap[74]->IsAlias = 0
+pp_data->cap[74]->IsStringRange = 0
+pp_data->cap[74]->IsDesignatorRange = 0
+pp_data->cap[74]->Reserved1 = 0x000000
+pp_data->cap[74]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[74]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[74]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[74]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[74]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[74]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[74]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[74]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[74]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[74]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[74]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[74]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[74]->NotRange.Usage = 0x0004
+pp_data->cap[74]->NotRange.Reserved1 = 0x0004
+pp_data->cap[74]->NotRange.StringIndex = 0
+pp_data->cap[74]->NotRange.Reserved2 = 0
+pp_data->cap[74]->NotRange.DesignatorIndex = 0
+pp_data->cap[74]->NotRange.Reserved3 = 0
+pp_data->cap[74]->NotRange.DataIndex = 74
+pp_data->cap[74]->NotRange.Reserved4 = 74
+pp_data->cap[74]->NotButton.HasNull = 0
+pp_data->cap[74]->NotButton.Reserved4 = 0x000000
+pp_data->cap[74]->NotButton.LogicalMin = 0
+pp_data->cap[74]->NotButton.LogicalMax = 4095
+pp_data->cap[74]->NotButton.PhysicalMin = 0
+pp_data->cap[74]->NotButton.PhysicalMax = 0
+pp_data->cap[74]->Units = 0
+pp_data->cap[74]->UnitsExp = 0
+
+pp_data->cap[75]->UsagePage = 0xFF01
+pp_data->cap[75]->ReportID = 0x02
+pp_data->cap[75]->BitPosition = 0
+pp_data->cap[75]->BitSize = 16
+pp_data->cap[75]->ReportCount = 1
+pp_data->cap[75]->BytePosition = 0x0015
+pp_data->cap[75]->BitCount = 16
+pp_data->cap[75]->BitField = 0x02
+pp_data->cap[75]->NextBytePosition = 0x0017
+pp_data->cap[75]->LinkCollection = 0x0002
+pp_data->cap[75]->LinkUsagePage = 0xFF01
+pp_data->cap[75]->LinkUsage = 0x0002
+pp_data->cap[75]->IsMultipleItemsForArray = 0
+pp_data->cap[75]->IsButtonCap = 0
+pp_data->cap[75]->IsPadding = 0
+pp_data->cap[75]->IsAbsolute = 1
+pp_data->cap[75]->IsRange = 0
+pp_data->cap[75]->IsAlias = 0
+pp_data->cap[75]->IsStringRange = 0
+pp_data->cap[75]->IsDesignatorRange = 0
+pp_data->cap[75]->Reserved1 = 0x000000
+pp_data->cap[75]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[75]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[75]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[75]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[75]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[75]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[75]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[75]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[75]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[75]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[75]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[75]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[75]->NotRange.Usage = 0x0004
+pp_data->cap[75]->NotRange.Reserved1 = 0x0004
+pp_data->cap[75]->NotRange.StringIndex = 0
+pp_data->cap[75]->NotRange.Reserved2 = 0
+pp_data->cap[75]->NotRange.DesignatorIndex = 0
+pp_data->cap[75]->NotRange.Reserved3 = 0
+pp_data->cap[75]->NotRange.DataIndex = 75
+pp_data->cap[75]->NotRange.Reserved4 = 75
+pp_data->cap[75]->NotButton.HasNull = 0
+pp_data->cap[75]->NotButton.Reserved4 = 0x000000
+pp_data->cap[75]->NotButton.LogicalMin = 0
+pp_data->cap[75]->NotButton.LogicalMax = 4095
+pp_data->cap[75]->NotButton.PhysicalMin = 0
+pp_data->cap[75]->NotButton.PhysicalMax = 0
+pp_data->cap[75]->Units = 0
+pp_data->cap[75]->UnitsExp = 0
+
+pp_data->cap[76]->UsagePage = 0xFF01
+pp_data->cap[76]->ReportID = 0x02
+pp_data->cap[76]->BitPosition = 0
+pp_data->cap[76]->BitSize = 16
+pp_data->cap[76]->ReportCount = 1
+pp_data->cap[76]->BytePosition = 0x0013
+pp_data->cap[76]->BitCount = 16
+pp_data->cap[76]->BitField = 0x02
+pp_data->cap[76]->NextBytePosition = 0x0015
+pp_data->cap[76]->LinkCollection = 0x0002
+pp_data->cap[76]->LinkUsagePage = 0xFF01
+pp_data->cap[76]->LinkUsage = 0x0002
+pp_data->cap[76]->IsMultipleItemsForArray = 0
+pp_data->cap[76]->IsButtonCap = 0
+pp_data->cap[76]->IsPadding = 0
+pp_data->cap[76]->IsAbsolute = 1
+pp_data->cap[76]->IsRange = 0
+pp_data->cap[76]->IsAlias = 0
+pp_data->cap[76]->IsStringRange = 0
+pp_data->cap[76]->IsDesignatorRange = 0
+pp_data->cap[76]->Reserved1 = 0x000000
+pp_data->cap[76]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[76]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[76]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[76]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[76]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[76]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[76]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[76]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[76]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[76]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[76]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[76]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[76]->NotRange.Usage = 0x0004
+pp_data->cap[76]->NotRange.Reserved1 = 0x0004
+pp_data->cap[76]->NotRange.StringIndex = 0
+pp_data->cap[76]->NotRange.Reserved2 = 0
+pp_data->cap[76]->NotRange.DesignatorIndex = 0
+pp_data->cap[76]->NotRange.Reserved3 = 0
+pp_data->cap[76]->NotRange.DataIndex = 76
+pp_data->cap[76]->NotRange.Reserved4 = 76
+pp_data->cap[76]->NotButton.HasNull = 0
+pp_data->cap[76]->NotButton.Reserved4 = 0x000000
+pp_data->cap[76]->NotButton.LogicalMin = 0
+pp_data->cap[76]->NotButton.LogicalMax = 4095
+pp_data->cap[76]->NotButton.PhysicalMin = 0
+pp_data->cap[76]->NotButton.PhysicalMax = 0
+pp_data->cap[76]->Units = 0
+pp_data->cap[76]->UnitsExp = 0
+
+pp_data->cap[77]->UsagePage = 0xFF01
+pp_data->cap[77]->ReportID = 0x02
+pp_data->cap[77]->BitPosition = 0
+pp_data->cap[77]->BitSize = 16
+pp_data->cap[77]->ReportCount = 1
+pp_data->cap[77]->BytePosition = 0x0011
+pp_data->cap[77]->BitCount = 16
+pp_data->cap[77]->BitField = 0x02
+pp_data->cap[77]->NextBytePosition = 0x0013
+pp_data->cap[77]->LinkCollection = 0x0002
+pp_data->cap[77]->LinkUsagePage = 0xFF01
+pp_data->cap[77]->LinkUsage = 0x0002
+pp_data->cap[77]->IsMultipleItemsForArray = 0
+pp_data->cap[77]->IsButtonCap = 0
+pp_data->cap[77]->IsPadding = 0
+pp_data->cap[77]->IsAbsolute = 1
+pp_data->cap[77]->IsRange = 0
+pp_data->cap[77]->IsAlias = 0
+pp_data->cap[77]->IsStringRange = 0
+pp_data->cap[77]->IsDesignatorRange = 0
+pp_data->cap[77]->Reserved1 = 0x000000
+pp_data->cap[77]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[77]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[77]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[77]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[77]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[77]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[77]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[77]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[77]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[77]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[77]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[77]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[77]->NotRange.Usage = 0x0004
+pp_data->cap[77]->NotRange.Reserved1 = 0x0004
+pp_data->cap[77]->NotRange.StringIndex = 0
+pp_data->cap[77]->NotRange.Reserved2 = 0
+pp_data->cap[77]->NotRange.DesignatorIndex = 0
+pp_data->cap[77]->NotRange.Reserved3 = 0
+pp_data->cap[77]->NotRange.DataIndex = 77
+pp_data->cap[77]->NotRange.Reserved4 = 77
+pp_data->cap[77]->NotButton.HasNull = 0
+pp_data->cap[77]->NotButton.Reserved4 = 0x000000
+pp_data->cap[77]->NotButton.LogicalMin = 0
+pp_data->cap[77]->NotButton.LogicalMax = 4095
+pp_data->cap[77]->NotButton.PhysicalMin = 0
+pp_data->cap[77]->NotButton.PhysicalMax = 0
+pp_data->cap[77]->Units = 0
+pp_data->cap[77]->UnitsExp = 0
+
+pp_data->cap[78]->UsagePage = 0xFF01
+pp_data->cap[78]->ReportID = 0x02
+pp_data->cap[78]->BitPosition = 0
+pp_data->cap[78]->BitSize = 16
+pp_data->cap[78]->ReportCount = 1
+pp_data->cap[78]->BytePosition = 0x000F
+pp_data->cap[78]->BitCount = 16
+pp_data->cap[78]->BitField = 0x02
+pp_data->cap[78]->NextBytePosition = 0x0011
+pp_data->cap[78]->LinkCollection = 0x0002
+pp_data->cap[78]->LinkUsagePage = 0xFF01
+pp_data->cap[78]->LinkUsage = 0x0002
+pp_data->cap[78]->IsMultipleItemsForArray = 0
+pp_data->cap[78]->IsButtonCap = 0
+pp_data->cap[78]->IsPadding = 0
+pp_data->cap[78]->IsAbsolute = 1
+pp_data->cap[78]->IsRange = 0
+pp_data->cap[78]->IsAlias = 0
+pp_data->cap[78]->IsStringRange = 0
+pp_data->cap[78]->IsDesignatorRange = 0
+pp_data->cap[78]->Reserved1 = 0x000000
+pp_data->cap[78]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[78]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[78]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[78]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[78]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[78]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[78]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[78]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[78]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[78]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[78]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[78]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[78]->NotRange.Usage = 0x0004
+pp_data->cap[78]->NotRange.Reserved1 = 0x0004
+pp_data->cap[78]->NotRange.StringIndex = 0
+pp_data->cap[78]->NotRange.Reserved2 = 0
+pp_data->cap[78]->NotRange.DesignatorIndex = 0
+pp_data->cap[78]->NotRange.Reserved3 = 0
+pp_data->cap[78]->NotRange.DataIndex = 78
+pp_data->cap[78]->NotRange.Reserved4 = 78
+pp_data->cap[78]->NotButton.HasNull = 0
+pp_data->cap[78]->NotButton.Reserved4 = 0x000000
+pp_data->cap[78]->NotButton.LogicalMin = 0
+pp_data->cap[78]->NotButton.LogicalMax = 4095
+pp_data->cap[78]->NotButton.PhysicalMin = 0
+pp_data->cap[78]->NotButton.PhysicalMax = 0
+pp_data->cap[78]->Units = 0
+pp_data->cap[78]->UnitsExp = 0
+
+pp_data->cap[79]->UsagePage = 0xFF01
+pp_data->cap[79]->ReportID = 0x02
+pp_data->cap[79]->BitPosition = 0
+pp_data->cap[79]->BitSize = 16
+pp_data->cap[79]->ReportCount = 1
+pp_data->cap[79]->BytePosition = 0x000D
+pp_data->cap[79]->BitCount = 16
+pp_data->cap[79]->BitField = 0x02
+pp_data->cap[79]->NextBytePosition = 0x000F
+pp_data->cap[79]->LinkCollection = 0x0002
+pp_data->cap[79]->LinkUsagePage = 0xFF01
+pp_data->cap[79]->LinkUsage = 0x0002
+pp_data->cap[79]->IsMultipleItemsForArray = 0
+pp_data->cap[79]->IsButtonCap = 0
+pp_data->cap[79]->IsPadding = 0
+pp_data->cap[79]->IsAbsolute = 1
+pp_data->cap[79]->IsRange = 0
+pp_data->cap[79]->IsAlias = 0
+pp_data->cap[79]->IsStringRange = 0
+pp_data->cap[79]->IsDesignatorRange = 0
+pp_data->cap[79]->Reserved1 = 0x000000
+pp_data->cap[79]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[79]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[79]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[79]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[79]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[79]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[79]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[79]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[79]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[79]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[79]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[79]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[79]->NotRange.Usage = 0x0004
+pp_data->cap[79]->NotRange.Reserved1 = 0x0004
+pp_data->cap[79]->NotRange.StringIndex = 0
+pp_data->cap[79]->NotRange.Reserved2 = 0
+pp_data->cap[79]->NotRange.DesignatorIndex = 0
+pp_data->cap[79]->NotRange.Reserved3 = 0
+pp_data->cap[79]->NotRange.DataIndex = 79
+pp_data->cap[79]->NotRange.Reserved4 = 79
+pp_data->cap[79]->NotButton.HasNull = 0
+pp_data->cap[79]->NotButton.Reserved4 = 0x000000
+pp_data->cap[79]->NotButton.LogicalMin = 0
+pp_data->cap[79]->NotButton.LogicalMax = 4095
+pp_data->cap[79]->NotButton.PhysicalMin = 0
+pp_data->cap[79]->NotButton.PhysicalMax = 0
+pp_data->cap[79]->Units = 0
+pp_data->cap[79]->UnitsExp = 0
+
+pp_data->cap[80]->UsagePage = 0xFF01
+pp_data->cap[80]->ReportID = 0x02
+pp_data->cap[80]->BitPosition = 0
+pp_data->cap[80]->BitSize = 16
+pp_data->cap[80]->ReportCount = 1
+pp_data->cap[80]->BytePosition = 0x000B
+pp_data->cap[80]->BitCount = 16
+pp_data->cap[80]->BitField = 0x02
+pp_data->cap[80]->NextBytePosition = 0x000D
+pp_data->cap[80]->LinkCollection = 0x0002
+pp_data->cap[80]->LinkUsagePage = 0xFF01
+pp_data->cap[80]->LinkUsage = 0x0002
+pp_data->cap[80]->IsMultipleItemsForArray = 0
+pp_data->cap[80]->IsButtonCap = 0
+pp_data->cap[80]->IsPadding = 0
+pp_data->cap[80]->IsAbsolute = 1
+pp_data->cap[80]->IsRange = 0
+pp_data->cap[80]->IsAlias = 0
+pp_data->cap[80]->IsStringRange = 0
+pp_data->cap[80]->IsDesignatorRange = 0
+pp_data->cap[80]->Reserved1 = 0x000000
+pp_data->cap[80]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[80]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[80]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[80]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[80]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[80]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[80]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[80]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[80]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[80]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[80]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[80]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[80]->NotRange.Usage = 0x0004
+pp_data->cap[80]->NotRange.Reserved1 = 0x0004
+pp_data->cap[80]->NotRange.StringIndex = 0
+pp_data->cap[80]->NotRange.Reserved2 = 0
+pp_data->cap[80]->NotRange.DesignatorIndex = 0
+pp_data->cap[80]->NotRange.Reserved3 = 0
+pp_data->cap[80]->NotRange.DataIndex = 80
+pp_data->cap[80]->NotRange.Reserved4 = 80
+pp_data->cap[80]->NotButton.HasNull = 0
+pp_data->cap[80]->NotButton.Reserved4 = 0x000000
+pp_data->cap[80]->NotButton.LogicalMin = 0
+pp_data->cap[80]->NotButton.LogicalMax = 4095
+pp_data->cap[80]->NotButton.PhysicalMin = 0
+pp_data->cap[80]->NotButton.PhysicalMax = 0
+pp_data->cap[80]->Units = 0
+pp_data->cap[80]->UnitsExp = 0
+
+pp_data->cap[81]->UsagePage = 0xFF01
+pp_data->cap[81]->ReportID = 0x02
+pp_data->cap[81]->BitPosition = 0
+pp_data->cap[81]->BitSize = 16
+pp_data->cap[81]->ReportCount = 1
+pp_data->cap[81]->BytePosition = 0x0009
+pp_data->cap[81]->BitCount = 16
+pp_data->cap[81]->BitField = 0x02
+pp_data->cap[81]->NextBytePosition = 0x000B
+pp_data->cap[81]->LinkCollection = 0x0002
+pp_data->cap[81]->LinkUsagePage = 0xFF01
+pp_data->cap[81]->LinkUsage = 0x0002
+pp_data->cap[81]->IsMultipleItemsForArray = 0
+pp_data->cap[81]->IsButtonCap = 0
+pp_data->cap[81]->IsPadding = 0
+pp_data->cap[81]->IsAbsolute = 1
+pp_data->cap[81]->IsRange = 0
+pp_data->cap[81]->IsAlias = 0
+pp_data->cap[81]->IsStringRange = 0
+pp_data->cap[81]->IsDesignatorRange = 0
+pp_data->cap[81]->Reserved1 = 0x000000
+pp_data->cap[81]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[81]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[81]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[81]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[81]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[81]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[81]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[81]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[81]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[81]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[81]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[81]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[81]->NotRange.Usage = 0x0004
+pp_data->cap[81]->NotRange.Reserved1 = 0x0004
+pp_data->cap[81]->NotRange.StringIndex = 0
+pp_data->cap[81]->NotRange.Reserved2 = 0
+pp_data->cap[81]->NotRange.DesignatorIndex = 0
+pp_data->cap[81]->NotRange.Reserved3 = 0
+pp_data->cap[81]->NotRange.DataIndex = 81
+pp_data->cap[81]->NotRange.Reserved4 = 81
+pp_data->cap[81]->NotButton.HasNull = 0
+pp_data->cap[81]->NotButton.Reserved4 = 0x000000
+pp_data->cap[81]->NotButton.LogicalMin = 0
+pp_data->cap[81]->NotButton.LogicalMax = 4095
+pp_data->cap[81]->NotButton.PhysicalMin = 0
+pp_data->cap[81]->NotButton.PhysicalMax = 0
+pp_data->cap[81]->Units = 0
+pp_data->cap[81]->UnitsExp = 0
+
+pp_data->cap[82]->UsagePage = 0xFF01
+pp_data->cap[82]->ReportID = 0x02
+pp_data->cap[82]->BitPosition = 0
+pp_data->cap[82]->BitSize = 16
+pp_data->cap[82]->ReportCount = 1
+pp_data->cap[82]->BytePosition = 0x0007
+pp_data->cap[82]->BitCount = 16
+pp_data->cap[82]->BitField = 0x02
+pp_data->cap[82]->NextBytePosition = 0x0009
+pp_data->cap[82]->LinkCollection = 0x0002
+pp_data->cap[82]->LinkUsagePage = 0xFF01
+pp_data->cap[82]->LinkUsage = 0x0002
+pp_data->cap[82]->IsMultipleItemsForArray = 0
+pp_data->cap[82]->IsButtonCap = 0
+pp_data->cap[82]->IsPadding = 0
+pp_data->cap[82]->IsAbsolute = 1
+pp_data->cap[82]->IsRange = 0
+pp_data->cap[82]->IsAlias = 0
+pp_data->cap[82]->IsStringRange = 0
+pp_data->cap[82]->IsDesignatorRange = 0
+pp_data->cap[82]->Reserved1 = 0x000000
+pp_data->cap[82]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[82]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[82]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[82]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[82]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[82]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[82]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[82]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[82]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[82]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[82]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[82]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[82]->NotRange.Usage = 0x0004
+pp_data->cap[82]->NotRange.Reserved1 = 0x0004
+pp_data->cap[82]->NotRange.StringIndex = 0
+pp_data->cap[82]->NotRange.Reserved2 = 0
+pp_data->cap[82]->NotRange.DesignatorIndex = 0
+pp_data->cap[82]->NotRange.Reserved3 = 0
+pp_data->cap[82]->NotRange.DataIndex = 82
+pp_data->cap[82]->NotRange.Reserved4 = 82
+pp_data->cap[82]->NotButton.HasNull = 0
+pp_data->cap[82]->NotButton.Reserved4 = 0x000000
+pp_data->cap[82]->NotButton.LogicalMin = 0
+pp_data->cap[82]->NotButton.LogicalMax = 4095
+pp_data->cap[82]->NotButton.PhysicalMin = 0
+pp_data->cap[82]->NotButton.PhysicalMax = 0
+pp_data->cap[82]->Units = 0
+pp_data->cap[82]->UnitsExp = 0
+
+pp_data->cap[83]->UsagePage = 0xFF01
+pp_data->cap[83]->ReportID = 0x02
+pp_data->cap[83]->BitPosition = 0
+pp_data->cap[83]->BitSize = 16
+pp_data->cap[83]->ReportCount = 1
+pp_data->cap[83]->BytePosition = 0x0005
+pp_data->cap[83]->BitCount = 16
+pp_data->cap[83]->BitField = 0x02
+pp_data->cap[83]->NextBytePosition = 0x0007
+pp_data->cap[83]->LinkCollection = 0x0002
+pp_data->cap[83]->LinkUsagePage = 0xFF01
+pp_data->cap[83]->LinkUsage = 0x0002
+pp_data->cap[83]->IsMultipleItemsForArray = 0
+pp_data->cap[83]->IsButtonCap = 0
+pp_data->cap[83]->IsPadding = 0
+pp_data->cap[83]->IsAbsolute = 1
+pp_data->cap[83]->IsRange = 0
+pp_data->cap[83]->IsAlias = 0
+pp_data->cap[83]->IsStringRange = 0
+pp_data->cap[83]->IsDesignatorRange = 0
+pp_data->cap[83]->Reserved1 = 0x000000
+pp_data->cap[83]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[83]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[83]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[83]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[83]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[83]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[83]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[83]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[83]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[83]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[83]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[83]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[83]->NotRange.Usage = 0x0004
+pp_data->cap[83]->NotRange.Reserved1 = 0x0004
+pp_data->cap[83]->NotRange.StringIndex = 0
+pp_data->cap[83]->NotRange.Reserved2 = 0
+pp_data->cap[83]->NotRange.DesignatorIndex = 0
+pp_data->cap[83]->NotRange.Reserved3 = 0
+pp_data->cap[83]->NotRange.DataIndex = 83
+pp_data->cap[83]->NotRange.Reserved4 = 83
+pp_data->cap[83]->NotButton.HasNull = 0
+pp_data->cap[83]->NotButton.Reserved4 = 0x000000
+pp_data->cap[83]->NotButton.LogicalMin = 0
+pp_data->cap[83]->NotButton.LogicalMax = 4095
+pp_data->cap[83]->NotButton.PhysicalMin = 0
+pp_data->cap[83]->NotButton.PhysicalMax = 0
+pp_data->cap[83]->Units = 0
+pp_data->cap[83]->UnitsExp = 0
+
+pp_data->cap[84]->UsagePage = 0xFF01
+pp_data->cap[84]->ReportID = 0x02
+pp_data->cap[84]->BitPosition = 0
+pp_data->cap[84]->BitSize = 16
+pp_data->cap[84]->ReportCount = 1
+pp_data->cap[84]->BytePosition = 0x0003
+pp_data->cap[84]->BitCount = 16
+pp_data->cap[84]->BitField = 0x02
+pp_data->cap[84]->NextBytePosition = 0x0005
+pp_data->cap[84]->LinkCollection = 0x0002
+pp_data->cap[84]->LinkUsagePage = 0xFF01
+pp_data->cap[84]->LinkUsage = 0x0002
+pp_data->cap[84]->IsMultipleItemsForArray = 0
+pp_data->cap[84]->IsButtonCap = 0
+pp_data->cap[84]->IsPadding = 0
+pp_data->cap[84]->IsAbsolute = 1
+pp_data->cap[84]->IsRange = 0
+pp_data->cap[84]->IsAlias = 0
+pp_data->cap[84]->IsStringRange = 0
+pp_data->cap[84]->IsDesignatorRange = 0
+pp_data->cap[84]->Reserved1 = 0x000000
+pp_data->cap[84]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[84]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[84]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[84]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[84]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[84]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[84]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[84]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[84]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[84]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[84]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[84]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[84]->NotRange.Usage = 0x0004
+pp_data->cap[84]->NotRange.Reserved1 = 0x0004
+pp_data->cap[84]->NotRange.StringIndex = 0
+pp_data->cap[84]->NotRange.Reserved2 = 0
+pp_data->cap[84]->NotRange.DesignatorIndex = 0
+pp_data->cap[84]->NotRange.Reserved3 = 0
+pp_data->cap[84]->NotRange.DataIndex = 84
+pp_data->cap[84]->NotRange.Reserved4 = 84
+pp_data->cap[84]->NotButton.HasNull = 0
+pp_data->cap[84]->NotButton.Reserved4 = 0x000000
+pp_data->cap[84]->NotButton.LogicalMin = 0
+pp_data->cap[84]->NotButton.LogicalMax = 4095
+pp_data->cap[84]->NotButton.PhysicalMin = 0
+pp_data->cap[84]->NotButton.PhysicalMax = 0
+pp_data->cap[84]->Units = 0
+pp_data->cap[84]->UnitsExp = 0
+
+pp_data->cap[85]->UsagePage = 0xFF01
+pp_data->cap[85]->ReportID = 0x02
+pp_data->cap[85]->BitPosition = 0
+pp_data->cap[85]->BitSize = 16
+pp_data->cap[85]->ReportCount = 1
+pp_data->cap[85]->BytePosition = 0x0001
+pp_data->cap[85]->BitCount = 16
+pp_data->cap[85]->BitField = 0x02
+pp_data->cap[85]->NextBytePosition = 0x0003
+pp_data->cap[85]->LinkCollection = 0x0002
+pp_data->cap[85]->LinkUsagePage = 0xFF01
+pp_data->cap[85]->LinkUsage = 0x0002
+pp_data->cap[85]->IsMultipleItemsForArray = 0
+pp_data->cap[85]->IsButtonCap = 0
+pp_data->cap[85]->IsPadding = 0
+pp_data->cap[85]->IsAbsolute = 1
+pp_data->cap[85]->IsRange = 0
+pp_data->cap[85]->IsAlias = 0
+pp_data->cap[85]->IsStringRange = 0
+pp_data->cap[85]->IsDesignatorRange = 0
+pp_data->cap[85]->Reserved1 = 0x000000
+pp_data->cap[85]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[85]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[85]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[85]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[85]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[85]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[85]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[85]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[85]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[85]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[85]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[85]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[85]->NotRange.Usage = 0x0004
+pp_data->cap[85]->NotRange.Reserved1 = 0x0004
+pp_data->cap[85]->NotRange.StringIndex = 0
+pp_data->cap[85]->NotRange.Reserved2 = 0
+pp_data->cap[85]->NotRange.DesignatorIndex = 0
+pp_data->cap[85]->NotRange.Reserved3 = 0
+pp_data->cap[85]->NotRange.DataIndex = 85
+pp_data->cap[85]->NotRange.Reserved4 = 85
+pp_data->cap[85]->NotButton.HasNull = 0
+pp_data->cap[85]->NotButton.Reserved4 = 0x000000
+pp_data->cap[85]->NotButton.LogicalMin = 0
+pp_data->cap[85]->NotButton.LogicalMax = 4095
+pp_data->cap[85]->NotButton.PhysicalMin = 0
+pp_data->cap[85]->NotButton.PhysicalMax = 0
+pp_data->cap[85]->Units = 0
+pp_data->cap[85]->UnitsExp = 0
+
+# Output hid_pp_cap struct:
+pp_data->cap[87]->UsagePage = 0xFF01
+pp_data->cap[87]->ReportID = 0x80
+pp_data->cap[87]->BitPosition = 0
+pp_data->cap[87]->BitSize = 8
+pp_data->cap[87]->ReportCount = 1
+pp_data->cap[87]->BytePosition = 0x005E
+pp_data->cap[87]->BitCount = 8
+pp_data->cap[87]->BitField = 0x02
+pp_data->cap[87]->NextBytePosition = 0x005F
+pp_data->cap[87]->LinkCollection = 0x0003
+pp_data->cap[87]->LinkUsagePage = 0xFF01
+pp_data->cap[87]->LinkUsage = 0x0080
+pp_data->cap[87]->IsMultipleItemsForArray = 0
+pp_data->cap[87]->IsButtonCap = 0
+pp_data->cap[87]->IsPadding = 0
+pp_data->cap[87]->IsAbsolute = 1
+pp_data->cap[87]->IsRange = 0
+pp_data->cap[87]->IsAlias = 0
+pp_data->cap[87]->IsStringRange = 0
+pp_data->cap[87]->IsDesignatorRange = 0
+pp_data->cap[87]->Reserved1 = 0x000000
+pp_data->cap[87]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[87]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[87]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[87]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[87]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[87]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[87]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[87]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[87]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[87]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[87]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[87]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[87]->NotRange.Usage = 0x0081
+pp_data->cap[87]->NotRange.Reserved1 = 0x0081
+pp_data->cap[87]->NotRange.StringIndex = 0
+pp_data->cap[87]->NotRange.Reserved2 = 0
+pp_data->cap[87]->NotRange.DesignatorIndex = 0
+pp_data->cap[87]->NotRange.Reserved3 = 0
+pp_data->cap[87]->NotRange.DataIndex = 0
+pp_data->cap[87]->NotRange.Reserved4 = 0
+pp_data->cap[87]->NotButton.HasNull = 0
+pp_data->cap[87]->NotButton.Reserved4 = 0x000000
+pp_data->cap[87]->NotButton.LogicalMin = 0
+pp_data->cap[87]->NotButton.LogicalMax = 127
+pp_data->cap[87]->NotButton.PhysicalMin = 0
+pp_data->cap[87]->NotButton.PhysicalMax = 0
+pp_data->cap[87]->Units = 0
+pp_data->cap[87]->UnitsExp = 0
+
+pp_data->cap[88]->UsagePage = 0xFF01
+pp_data->cap[88]->ReportID = 0x80
+pp_data->cap[88]->BitPosition = 0
+pp_data->cap[88]->BitSize = 8
+pp_data->cap[88]->ReportCount = 1
+pp_data->cap[88]->BytePosition = 0x005D
+pp_data->cap[88]->BitCount = 8
+pp_data->cap[88]->BitField = 0x02
+pp_data->cap[88]->NextBytePosition = 0x005E
+pp_data->cap[88]->LinkCollection = 0x0003
+pp_data->cap[88]->LinkUsagePage = 0xFF01
+pp_data->cap[88]->LinkUsage = 0x0080
+pp_data->cap[88]->IsMultipleItemsForArray = 0
+pp_data->cap[88]->IsButtonCap = 0
+pp_data->cap[88]->IsPadding = 0
+pp_data->cap[88]->IsAbsolute = 1
+pp_data->cap[88]->IsRange = 0
+pp_data->cap[88]->IsAlias = 0
+pp_data->cap[88]->IsStringRange = 0
+pp_data->cap[88]->IsDesignatorRange = 0
+pp_data->cap[88]->Reserved1 = 0x000000
+pp_data->cap[88]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[88]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[88]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[88]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[88]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[88]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[88]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[88]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[88]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[88]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[88]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[88]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[88]->NotRange.Usage = 0x0081
+pp_data->cap[88]->NotRange.Reserved1 = 0x0081
+pp_data->cap[88]->NotRange.StringIndex = 0
+pp_data->cap[88]->NotRange.Reserved2 = 0
+pp_data->cap[88]->NotRange.DesignatorIndex = 0
+pp_data->cap[88]->NotRange.Reserved3 = 0
+pp_data->cap[88]->NotRange.DataIndex = 1
+pp_data->cap[88]->NotRange.Reserved4 = 1
+pp_data->cap[88]->NotButton.HasNull = 0
+pp_data->cap[88]->NotButton.Reserved4 = 0x000000
+pp_data->cap[88]->NotButton.LogicalMin = 0
+pp_data->cap[88]->NotButton.LogicalMax = 127
+pp_data->cap[88]->NotButton.PhysicalMin = 0
+pp_data->cap[88]->NotButton.PhysicalMax = 0
+pp_data->cap[88]->Units = 0
+pp_data->cap[88]->UnitsExp = 0
+
+pp_data->cap[89]->UsagePage = 0xFF01
+pp_data->cap[89]->ReportID = 0x80
+pp_data->cap[89]->BitPosition = 0
+pp_data->cap[89]->BitSize = 8
+pp_data->cap[89]->ReportCount = 1
+pp_data->cap[89]->BytePosition = 0x005C
+pp_data->cap[89]->BitCount = 8
+pp_data->cap[89]->BitField = 0x02
+pp_data->cap[89]->NextBytePosition = 0x005D
+pp_data->cap[89]->LinkCollection = 0x0003
+pp_data->cap[89]->LinkUsagePage = 0xFF01
+pp_data->cap[89]->LinkUsage = 0x0080
+pp_data->cap[89]->IsMultipleItemsForArray = 0
+pp_data->cap[89]->IsButtonCap = 0
+pp_data->cap[89]->IsPadding = 0
+pp_data->cap[89]->IsAbsolute = 1
+pp_data->cap[89]->IsRange = 0
+pp_data->cap[89]->IsAlias = 0
+pp_data->cap[89]->IsStringRange = 0
+pp_data->cap[89]->IsDesignatorRange = 0
+pp_data->cap[89]->Reserved1 = 0x000000
+pp_data->cap[89]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[89]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[89]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[89]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[89]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[89]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[89]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[89]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[89]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[89]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[89]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[89]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[89]->NotRange.Usage = 0x0081
+pp_data->cap[89]->NotRange.Reserved1 = 0x0081
+pp_data->cap[89]->NotRange.StringIndex = 0
+pp_data->cap[89]->NotRange.Reserved2 = 0
+pp_data->cap[89]->NotRange.DesignatorIndex = 0
+pp_data->cap[89]->NotRange.Reserved3 = 0
+pp_data->cap[89]->NotRange.DataIndex = 2
+pp_data->cap[89]->NotRange.Reserved4 = 2
+pp_data->cap[89]->NotButton.HasNull = 0
+pp_data->cap[89]->NotButton.Reserved4 = 0x000000
+pp_data->cap[89]->NotButton.LogicalMin = 0
+pp_data->cap[89]->NotButton.LogicalMax = 127
+pp_data->cap[89]->NotButton.PhysicalMin = 0
+pp_data->cap[89]->NotButton.PhysicalMax = 0
+pp_data->cap[89]->Units = 0
+pp_data->cap[89]->UnitsExp = 0
+
+pp_data->cap[90]->UsagePage = 0xFF01
+pp_data->cap[90]->ReportID = 0x80
+pp_data->cap[90]->BitPosition = 0
+pp_data->cap[90]->BitSize = 8
+pp_data->cap[90]->ReportCount = 1
+pp_data->cap[90]->BytePosition = 0x005B
+pp_data->cap[90]->BitCount = 8
+pp_data->cap[90]->BitField = 0x02
+pp_data->cap[90]->NextBytePosition = 0x005C
+pp_data->cap[90]->LinkCollection = 0x0003
+pp_data->cap[90]->LinkUsagePage = 0xFF01
+pp_data->cap[90]->LinkUsage = 0x0080
+pp_data->cap[90]->IsMultipleItemsForArray = 0
+pp_data->cap[90]->IsButtonCap = 0
+pp_data->cap[90]->IsPadding = 0
+pp_data->cap[90]->IsAbsolute = 1
+pp_data->cap[90]->IsRange = 0
+pp_data->cap[90]->IsAlias = 0
+pp_data->cap[90]->IsStringRange = 0
+pp_data->cap[90]->IsDesignatorRange = 0
+pp_data->cap[90]->Reserved1 = 0x000000
+pp_data->cap[90]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[90]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[90]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[90]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[90]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[90]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[90]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[90]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[90]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[90]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[90]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[90]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[90]->NotRange.Usage = 0x0081
+pp_data->cap[90]->NotRange.Reserved1 = 0x0081
+pp_data->cap[90]->NotRange.StringIndex = 0
+pp_data->cap[90]->NotRange.Reserved2 = 0
+pp_data->cap[90]->NotRange.DesignatorIndex = 0
+pp_data->cap[90]->NotRange.Reserved3 = 0
+pp_data->cap[90]->NotRange.DataIndex = 3
+pp_data->cap[90]->NotRange.Reserved4 = 3
+pp_data->cap[90]->NotButton.HasNull = 0
+pp_data->cap[90]->NotButton.Reserved4 = 0x000000
+pp_data->cap[90]->NotButton.LogicalMin = 0
+pp_data->cap[90]->NotButton.LogicalMax = 127
+pp_data->cap[90]->NotButton.PhysicalMin = 0
+pp_data->cap[90]->NotButton.PhysicalMax = 0
+pp_data->cap[90]->Units = 0
+pp_data->cap[90]->UnitsExp = 0
+
+pp_data->cap[91]->UsagePage = 0xFF01
+pp_data->cap[91]->ReportID = 0x80
+pp_data->cap[91]->BitPosition = 0
+pp_data->cap[91]->BitSize = 8
+pp_data->cap[91]->ReportCount = 1
+pp_data->cap[91]->BytePosition = 0x005A
+pp_data->cap[91]->BitCount = 8
+pp_data->cap[91]->BitField = 0x02
+pp_data->cap[91]->NextBytePosition = 0x005B
+pp_data->cap[91]->LinkCollection = 0x0003
+pp_data->cap[91]->LinkUsagePage = 0xFF01
+pp_data->cap[91]->LinkUsage = 0x0080
+pp_data->cap[91]->IsMultipleItemsForArray = 0
+pp_data->cap[91]->IsButtonCap = 0
+pp_data->cap[91]->IsPadding = 0
+pp_data->cap[91]->IsAbsolute = 1
+pp_data->cap[91]->IsRange = 0
+pp_data->cap[91]->IsAlias = 0
+pp_data->cap[91]->IsStringRange = 0
+pp_data->cap[91]->IsDesignatorRange = 0
+pp_data->cap[91]->Reserved1 = 0x000000
+pp_data->cap[91]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[91]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[91]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[91]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[91]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[91]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[91]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[91]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[91]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[91]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[91]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[91]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[91]->NotRange.Usage = 0x0081
+pp_data->cap[91]->NotRange.Reserved1 = 0x0081
+pp_data->cap[91]->NotRange.StringIndex = 0
+pp_data->cap[91]->NotRange.Reserved2 = 0
+pp_data->cap[91]->NotRange.DesignatorIndex = 0
+pp_data->cap[91]->NotRange.Reserved3 = 0
+pp_data->cap[91]->NotRange.DataIndex = 4
+pp_data->cap[91]->NotRange.Reserved4 = 4
+pp_data->cap[91]->NotButton.HasNull = 0
+pp_data->cap[91]->NotButton.Reserved4 = 0x000000
+pp_data->cap[91]->NotButton.LogicalMin = 0
+pp_data->cap[91]->NotButton.LogicalMax = 127
+pp_data->cap[91]->NotButton.PhysicalMin = 0
+pp_data->cap[91]->NotButton.PhysicalMax = 0
+pp_data->cap[91]->Units = 0
+pp_data->cap[91]->UnitsExp = 0
+
+pp_data->cap[92]->UsagePage = 0xFF01
+pp_data->cap[92]->ReportID = 0x80
+pp_data->cap[92]->BitPosition = 0
+pp_data->cap[92]->BitSize = 8
+pp_data->cap[92]->ReportCount = 1
+pp_data->cap[92]->BytePosition = 0x0059
+pp_data->cap[92]->BitCount = 8
+pp_data->cap[92]->BitField = 0x02
+pp_data->cap[92]->NextBytePosition = 0x005A
+pp_data->cap[92]->LinkCollection = 0x0003
+pp_data->cap[92]->LinkUsagePage = 0xFF01
+pp_data->cap[92]->LinkUsage = 0x0080
+pp_data->cap[92]->IsMultipleItemsForArray = 0
+pp_data->cap[92]->IsButtonCap = 0
+pp_data->cap[92]->IsPadding = 0
+pp_data->cap[92]->IsAbsolute = 1
+pp_data->cap[92]->IsRange = 0
+pp_data->cap[92]->IsAlias = 0
+pp_data->cap[92]->IsStringRange = 0
+pp_data->cap[92]->IsDesignatorRange = 0
+pp_data->cap[92]->Reserved1 = 0x000000
+pp_data->cap[92]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[92]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[92]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[92]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[92]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[92]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[92]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[92]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[92]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[92]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[92]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[92]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[92]->NotRange.Usage = 0x0081
+pp_data->cap[92]->NotRange.Reserved1 = 0x0081
+pp_data->cap[92]->NotRange.StringIndex = 0
+pp_data->cap[92]->NotRange.Reserved2 = 0
+pp_data->cap[92]->NotRange.DesignatorIndex = 0
+pp_data->cap[92]->NotRange.Reserved3 = 0
+pp_data->cap[92]->NotRange.DataIndex = 5
+pp_data->cap[92]->NotRange.Reserved4 = 5
+pp_data->cap[92]->NotButton.HasNull = 0
+pp_data->cap[92]->NotButton.Reserved4 = 0x000000
+pp_data->cap[92]->NotButton.LogicalMin = 0
+pp_data->cap[92]->NotButton.LogicalMax = 127
+pp_data->cap[92]->NotButton.PhysicalMin = 0
+pp_data->cap[92]->NotButton.PhysicalMax = 0
+pp_data->cap[92]->Units = 0
+pp_data->cap[92]->UnitsExp = 0
+
+pp_data->cap[93]->UsagePage = 0xFF01
+pp_data->cap[93]->ReportID = 0x80
+pp_data->cap[93]->BitPosition = 0
+pp_data->cap[93]->BitSize = 8
+pp_data->cap[93]->ReportCount = 1
+pp_data->cap[93]->BytePosition = 0x0058
+pp_data->cap[93]->BitCount = 8
+pp_data->cap[93]->BitField = 0x02
+pp_data->cap[93]->NextBytePosition = 0x0059
+pp_data->cap[93]->LinkCollection = 0x0003
+pp_data->cap[93]->LinkUsagePage = 0xFF01
+pp_data->cap[93]->LinkUsage = 0x0080
+pp_data->cap[93]->IsMultipleItemsForArray = 0
+pp_data->cap[93]->IsButtonCap = 0
+pp_data->cap[93]->IsPadding = 0
+pp_data->cap[93]->IsAbsolute = 1
+pp_data->cap[93]->IsRange = 0
+pp_data->cap[93]->IsAlias = 0
+pp_data->cap[93]->IsStringRange = 0
+pp_data->cap[93]->IsDesignatorRange = 0
+pp_data->cap[93]->Reserved1 = 0x000000
+pp_data->cap[93]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[93]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[93]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[93]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[93]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[93]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[93]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[93]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[93]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[93]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[93]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[93]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[93]->NotRange.Usage = 0x0081
+pp_data->cap[93]->NotRange.Reserved1 = 0x0081
+pp_data->cap[93]->NotRange.StringIndex = 0
+pp_data->cap[93]->NotRange.Reserved2 = 0
+pp_data->cap[93]->NotRange.DesignatorIndex = 0
+pp_data->cap[93]->NotRange.Reserved3 = 0
+pp_data->cap[93]->NotRange.DataIndex = 6
+pp_data->cap[93]->NotRange.Reserved4 = 6
+pp_data->cap[93]->NotButton.HasNull = 0
+pp_data->cap[93]->NotButton.Reserved4 = 0x000000
+pp_data->cap[93]->NotButton.LogicalMin = 0
+pp_data->cap[93]->NotButton.LogicalMax = 127
+pp_data->cap[93]->NotButton.PhysicalMin = 0
+pp_data->cap[93]->NotButton.PhysicalMax = 0
+pp_data->cap[93]->Units = 0
+pp_data->cap[93]->UnitsExp = 0
+
+pp_data->cap[94]->UsagePage = 0xFF01
+pp_data->cap[94]->ReportID = 0x80
+pp_data->cap[94]->BitPosition = 0
+pp_data->cap[94]->BitSize = 8
+pp_data->cap[94]->ReportCount = 1
+pp_data->cap[94]->BytePosition = 0x0057
+pp_data->cap[94]->BitCount = 8
+pp_data->cap[94]->BitField = 0x02
+pp_data->cap[94]->NextBytePosition = 0x0058
+pp_data->cap[94]->LinkCollection = 0x0003
+pp_data->cap[94]->LinkUsagePage = 0xFF01
+pp_data->cap[94]->LinkUsage = 0x0080
+pp_data->cap[94]->IsMultipleItemsForArray = 0
+pp_data->cap[94]->IsButtonCap = 0
+pp_data->cap[94]->IsPadding = 0
+pp_data->cap[94]->IsAbsolute = 1
+pp_data->cap[94]->IsRange = 0
+pp_data->cap[94]->IsAlias = 0
+pp_data->cap[94]->IsStringRange = 0
+pp_data->cap[94]->IsDesignatorRange = 0
+pp_data->cap[94]->Reserved1 = 0x000000
+pp_data->cap[94]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[94]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[94]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[94]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[94]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[94]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[94]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[94]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[94]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[94]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[94]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[94]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[94]->NotRange.Usage = 0x0081
+pp_data->cap[94]->NotRange.Reserved1 = 0x0081
+pp_data->cap[94]->NotRange.StringIndex = 0
+pp_data->cap[94]->NotRange.Reserved2 = 0
+pp_data->cap[94]->NotRange.DesignatorIndex = 0
+pp_data->cap[94]->NotRange.Reserved3 = 0
+pp_data->cap[94]->NotRange.DataIndex = 7
+pp_data->cap[94]->NotRange.Reserved4 = 7
+pp_data->cap[94]->NotButton.HasNull = 0
+pp_data->cap[94]->NotButton.Reserved4 = 0x000000
+pp_data->cap[94]->NotButton.LogicalMin = 0
+pp_data->cap[94]->NotButton.LogicalMax = 127
+pp_data->cap[94]->NotButton.PhysicalMin = 0
+pp_data->cap[94]->NotButton.PhysicalMax = 0
+pp_data->cap[94]->Units = 0
+pp_data->cap[94]->UnitsExp = 0
+
+pp_data->cap[95]->UsagePage = 0xFF01
+pp_data->cap[95]->ReportID = 0x80
+pp_data->cap[95]->BitPosition = 0
+pp_data->cap[95]->BitSize = 8
+pp_data->cap[95]->ReportCount = 1
+pp_data->cap[95]->BytePosition = 0x0056
+pp_data->cap[95]->BitCount = 8
+pp_data->cap[95]->BitField = 0x02
+pp_data->cap[95]->NextBytePosition = 0x0057
+pp_data->cap[95]->LinkCollection = 0x0003
+pp_data->cap[95]->LinkUsagePage = 0xFF01
+pp_data->cap[95]->LinkUsage = 0x0080
+pp_data->cap[95]->IsMultipleItemsForArray = 0
+pp_data->cap[95]->IsButtonCap = 0
+pp_data->cap[95]->IsPadding = 0
+pp_data->cap[95]->IsAbsolute = 1
+pp_data->cap[95]->IsRange = 0
+pp_data->cap[95]->IsAlias = 0
+pp_data->cap[95]->IsStringRange = 0
+pp_data->cap[95]->IsDesignatorRange = 0
+pp_data->cap[95]->Reserved1 = 0x000000
+pp_data->cap[95]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[95]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[95]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[95]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[95]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[95]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[95]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[95]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[95]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[95]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[95]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[95]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[95]->NotRange.Usage = 0x0081
+pp_data->cap[95]->NotRange.Reserved1 = 0x0081
+pp_data->cap[95]->NotRange.StringIndex = 0
+pp_data->cap[95]->NotRange.Reserved2 = 0
+pp_data->cap[95]->NotRange.DesignatorIndex = 0
+pp_data->cap[95]->NotRange.Reserved3 = 0
+pp_data->cap[95]->NotRange.DataIndex = 8
+pp_data->cap[95]->NotRange.Reserved4 = 8
+pp_data->cap[95]->NotButton.HasNull = 0
+pp_data->cap[95]->NotButton.Reserved4 = 0x000000
+pp_data->cap[95]->NotButton.LogicalMin = 0
+pp_data->cap[95]->NotButton.LogicalMax = 127
+pp_data->cap[95]->NotButton.PhysicalMin = 0
+pp_data->cap[95]->NotButton.PhysicalMax = 0
+pp_data->cap[95]->Units = 0
+pp_data->cap[95]->UnitsExp = 0
+
+pp_data->cap[96]->UsagePage = 0xFF01
+pp_data->cap[96]->ReportID = 0x80
+pp_data->cap[96]->BitPosition = 0
+pp_data->cap[96]->BitSize = 8
+pp_data->cap[96]->ReportCount = 1
+pp_data->cap[96]->BytePosition = 0x0055
+pp_data->cap[96]->BitCount = 8
+pp_data->cap[96]->BitField = 0x02
+pp_data->cap[96]->NextBytePosition = 0x0056
+pp_data->cap[96]->LinkCollection = 0x0003
+pp_data->cap[96]->LinkUsagePage = 0xFF01
+pp_data->cap[96]->LinkUsage = 0x0080
+pp_data->cap[96]->IsMultipleItemsForArray = 0
+pp_data->cap[96]->IsButtonCap = 0
+pp_data->cap[96]->IsPadding = 0
+pp_data->cap[96]->IsAbsolute = 1
+pp_data->cap[96]->IsRange = 0
+pp_data->cap[96]->IsAlias = 0
+pp_data->cap[96]->IsStringRange = 0
+pp_data->cap[96]->IsDesignatorRange = 0
+pp_data->cap[96]->Reserved1 = 0x000000
+pp_data->cap[96]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[96]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[96]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[96]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[96]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[96]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[96]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[96]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[96]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[96]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[96]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[96]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[96]->NotRange.Usage = 0x0081
+pp_data->cap[96]->NotRange.Reserved1 = 0x0081
+pp_data->cap[96]->NotRange.StringIndex = 0
+pp_data->cap[96]->NotRange.Reserved2 = 0
+pp_data->cap[96]->NotRange.DesignatorIndex = 0
+pp_data->cap[96]->NotRange.Reserved3 = 0
+pp_data->cap[96]->NotRange.DataIndex = 9
+pp_data->cap[96]->NotRange.Reserved4 = 9
+pp_data->cap[96]->NotButton.HasNull = 0
+pp_data->cap[96]->NotButton.Reserved4 = 0x000000
+pp_data->cap[96]->NotButton.LogicalMin = 0
+pp_data->cap[96]->NotButton.LogicalMax = 127
+pp_data->cap[96]->NotButton.PhysicalMin = 0
+pp_data->cap[96]->NotButton.PhysicalMax = 0
+pp_data->cap[96]->Units = 0
+pp_data->cap[96]->UnitsExp = 0
+
+pp_data->cap[97]->UsagePage = 0xFF01
+pp_data->cap[97]->ReportID = 0x80
+pp_data->cap[97]->BitPosition = 0
+pp_data->cap[97]->BitSize = 8
+pp_data->cap[97]->ReportCount = 1
+pp_data->cap[97]->BytePosition = 0x0054
+pp_data->cap[97]->BitCount = 8
+pp_data->cap[97]->BitField = 0x02
+pp_data->cap[97]->NextBytePosition = 0x0055
+pp_data->cap[97]->LinkCollection = 0x0003
+pp_data->cap[97]->LinkUsagePage = 0xFF01
+pp_data->cap[97]->LinkUsage = 0x0080
+pp_data->cap[97]->IsMultipleItemsForArray = 0
+pp_data->cap[97]->IsButtonCap = 0
+pp_data->cap[97]->IsPadding = 0
+pp_data->cap[97]->IsAbsolute = 1
+pp_data->cap[97]->IsRange = 0
+pp_data->cap[97]->IsAlias = 0
+pp_data->cap[97]->IsStringRange = 0
+pp_data->cap[97]->IsDesignatorRange = 0
+pp_data->cap[97]->Reserved1 = 0x000000
+pp_data->cap[97]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[97]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[97]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[97]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[97]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[97]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[97]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[97]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[97]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[97]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[97]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[97]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[97]->NotRange.Usage = 0x0081
+pp_data->cap[97]->NotRange.Reserved1 = 0x0081
+pp_data->cap[97]->NotRange.StringIndex = 0
+pp_data->cap[97]->NotRange.Reserved2 = 0
+pp_data->cap[97]->NotRange.DesignatorIndex = 0
+pp_data->cap[97]->NotRange.Reserved3 = 0
+pp_data->cap[97]->NotRange.DataIndex = 10
+pp_data->cap[97]->NotRange.Reserved4 = 10
+pp_data->cap[97]->NotButton.HasNull = 0
+pp_data->cap[97]->NotButton.Reserved4 = 0x000000
+pp_data->cap[97]->NotButton.LogicalMin = 0
+pp_data->cap[97]->NotButton.LogicalMax = 127
+pp_data->cap[97]->NotButton.PhysicalMin = 0
+pp_data->cap[97]->NotButton.PhysicalMax = 0
+pp_data->cap[97]->Units = 0
+pp_data->cap[97]->UnitsExp = 0
+
+pp_data->cap[98]->UsagePage = 0xFF01
+pp_data->cap[98]->ReportID = 0x80
+pp_data->cap[98]->BitPosition = 0
+pp_data->cap[98]->BitSize = 8
+pp_data->cap[98]->ReportCount = 1
+pp_data->cap[98]->BytePosition = 0x0053
+pp_data->cap[98]->BitCount = 8
+pp_data->cap[98]->BitField = 0x02
+pp_data->cap[98]->NextBytePosition = 0x0054
+pp_data->cap[98]->LinkCollection = 0x0003
+pp_data->cap[98]->LinkUsagePage = 0xFF01
+pp_data->cap[98]->LinkUsage = 0x0080
+pp_data->cap[98]->IsMultipleItemsForArray = 0
+pp_data->cap[98]->IsButtonCap = 0
+pp_data->cap[98]->IsPadding = 0
+pp_data->cap[98]->IsAbsolute = 1
+pp_data->cap[98]->IsRange = 0
+pp_data->cap[98]->IsAlias = 0
+pp_data->cap[98]->IsStringRange = 0
+pp_data->cap[98]->IsDesignatorRange = 0
+pp_data->cap[98]->Reserved1 = 0x000000
+pp_data->cap[98]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[98]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[98]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[98]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[98]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[98]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[98]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[98]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[98]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[98]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[98]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[98]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[98]->NotRange.Usage = 0x0081
+pp_data->cap[98]->NotRange.Reserved1 = 0x0081
+pp_data->cap[98]->NotRange.StringIndex = 0
+pp_data->cap[98]->NotRange.Reserved2 = 0
+pp_data->cap[98]->NotRange.DesignatorIndex = 0
+pp_data->cap[98]->NotRange.Reserved3 = 0
+pp_data->cap[98]->NotRange.DataIndex = 11
+pp_data->cap[98]->NotRange.Reserved4 = 11
+pp_data->cap[98]->NotButton.HasNull = 0
+pp_data->cap[98]->NotButton.Reserved4 = 0x000000
+pp_data->cap[98]->NotButton.LogicalMin = 0
+pp_data->cap[98]->NotButton.LogicalMax = 127
+pp_data->cap[98]->NotButton.PhysicalMin = 0
+pp_data->cap[98]->NotButton.PhysicalMax = 0
+pp_data->cap[98]->Units = 0
+pp_data->cap[98]->UnitsExp = 0
+
+pp_data->cap[99]->UsagePage = 0xFF01
+pp_data->cap[99]->ReportID = 0x80
+pp_data->cap[99]->BitPosition = 0
+pp_data->cap[99]->BitSize = 8
+pp_data->cap[99]->ReportCount = 1
+pp_data->cap[99]->BytePosition = 0x0052
+pp_data->cap[99]->BitCount = 8
+pp_data->cap[99]->BitField = 0x02
+pp_data->cap[99]->NextBytePosition = 0x0053
+pp_data->cap[99]->LinkCollection = 0x0003
+pp_data->cap[99]->LinkUsagePage = 0xFF01
+pp_data->cap[99]->LinkUsage = 0x0080
+pp_data->cap[99]->IsMultipleItemsForArray = 0
+pp_data->cap[99]->IsButtonCap = 0
+pp_data->cap[99]->IsPadding = 0
+pp_data->cap[99]->IsAbsolute = 1
+pp_data->cap[99]->IsRange = 0
+pp_data->cap[99]->IsAlias = 0
+pp_data->cap[99]->IsStringRange = 0
+pp_data->cap[99]->IsDesignatorRange = 0
+pp_data->cap[99]->Reserved1 = 0x000000
+pp_data->cap[99]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[99]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[99]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[99]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[99]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[99]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[99]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[99]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[99]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[99]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[99]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[99]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[99]->NotRange.Usage = 0x0081
+pp_data->cap[99]->NotRange.Reserved1 = 0x0081
+pp_data->cap[99]->NotRange.StringIndex = 0
+pp_data->cap[99]->NotRange.Reserved2 = 0
+pp_data->cap[99]->NotRange.DesignatorIndex = 0
+pp_data->cap[99]->NotRange.Reserved3 = 0
+pp_data->cap[99]->NotRange.DataIndex = 12
+pp_data->cap[99]->NotRange.Reserved4 = 12
+pp_data->cap[99]->NotButton.HasNull = 0
+pp_data->cap[99]->NotButton.Reserved4 = 0x000000
+pp_data->cap[99]->NotButton.LogicalMin = 0
+pp_data->cap[99]->NotButton.LogicalMax = 127
+pp_data->cap[99]->NotButton.PhysicalMin = 0
+pp_data->cap[99]->NotButton.PhysicalMax = 0
+pp_data->cap[99]->Units = 0
+pp_data->cap[99]->UnitsExp = 0
+
+pp_data->cap[100]->UsagePage = 0xFF01
+pp_data->cap[100]->ReportID = 0x80
+pp_data->cap[100]->BitPosition = 0
+pp_data->cap[100]->BitSize = 8
+pp_data->cap[100]->ReportCount = 1
+pp_data->cap[100]->BytePosition = 0x0051
+pp_data->cap[100]->BitCount = 8
+pp_data->cap[100]->BitField = 0x02
+pp_data->cap[100]->NextBytePosition = 0x0052
+pp_data->cap[100]->LinkCollection = 0x0003
+pp_data->cap[100]->LinkUsagePage = 0xFF01
+pp_data->cap[100]->LinkUsage = 0x0080
+pp_data->cap[100]->IsMultipleItemsForArray = 0
+pp_data->cap[100]->IsButtonCap = 0
+pp_data->cap[100]->IsPadding = 0
+pp_data->cap[100]->IsAbsolute = 1
+pp_data->cap[100]->IsRange = 0
+pp_data->cap[100]->IsAlias = 0
+pp_data->cap[100]->IsStringRange = 0
+pp_data->cap[100]->IsDesignatorRange = 0
+pp_data->cap[100]->Reserved1 = 0x000000
+pp_data->cap[100]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[100]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[100]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[100]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[100]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[100]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[100]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[100]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[100]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[100]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[100]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[100]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[100]->NotRange.Usage = 0x0081
+pp_data->cap[100]->NotRange.Reserved1 = 0x0081
+pp_data->cap[100]->NotRange.StringIndex = 0
+pp_data->cap[100]->NotRange.Reserved2 = 0
+pp_data->cap[100]->NotRange.DesignatorIndex = 0
+pp_data->cap[100]->NotRange.Reserved3 = 0
+pp_data->cap[100]->NotRange.DataIndex = 13
+pp_data->cap[100]->NotRange.Reserved4 = 13
+pp_data->cap[100]->NotButton.HasNull = 0
+pp_data->cap[100]->NotButton.Reserved4 = 0x000000
+pp_data->cap[100]->NotButton.LogicalMin = 0
+pp_data->cap[100]->NotButton.LogicalMax = 127
+pp_data->cap[100]->NotButton.PhysicalMin = 0
+pp_data->cap[100]->NotButton.PhysicalMax = 0
+pp_data->cap[100]->Units = 0
+pp_data->cap[100]->UnitsExp = 0
+
+pp_data->cap[101]->UsagePage = 0xFF01
+pp_data->cap[101]->ReportID = 0x80
+pp_data->cap[101]->BitPosition = 0
+pp_data->cap[101]->BitSize = 8
+pp_data->cap[101]->ReportCount = 1
+pp_data->cap[101]->BytePosition = 0x0050
+pp_data->cap[101]->BitCount = 8
+pp_data->cap[101]->BitField = 0x02
+pp_data->cap[101]->NextBytePosition = 0x0051
+pp_data->cap[101]->LinkCollection = 0x0003
+pp_data->cap[101]->LinkUsagePage = 0xFF01
+pp_data->cap[101]->LinkUsage = 0x0080
+pp_data->cap[101]->IsMultipleItemsForArray = 0
+pp_data->cap[101]->IsButtonCap = 0
+pp_data->cap[101]->IsPadding = 0
+pp_data->cap[101]->IsAbsolute = 1
+pp_data->cap[101]->IsRange = 0
+pp_data->cap[101]->IsAlias = 0
+pp_data->cap[101]->IsStringRange = 0
+pp_data->cap[101]->IsDesignatorRange = 0
+pp_data->cap[101]->Reserved1 = 0x000000
+pp_data->cap[101]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[101]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[101]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[101]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[101]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[101]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[101]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[101]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[101]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[101]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[101]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[101]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[101]->NotRange.Usage = 0x0081
+pp_data->cap[101]->NotRange.Reserved1 = 0x0081
+pp_data->cap[101]->NotRange.StringIndex = 0
+pp_data->cap[101]->NotRange.Reserved2 = 0
+pp_data->cap[101]->NotRange.DesignatorIndex = 0
+pp_data->cap[101]->NotRange.Reserved3 = 0
+pp_data->cap[101]->NotRange.DataIndex = 14
+pp_data->cap[101]->NotRange.Reserved4 = 14
+pp_data->cap[101]->NotButton.HasNull = 0
+pp_data->cap[101]->NotButton.Reserved4 = 0x000000
+pp_data->cap[101]->NotButton.LogicalMin = 0
+pp_data->cap[101]->NotButton.LogicalMax = 127
+pp_data->cap[101]->NotButton.PhysicalMin = 0
+pp_data->cap[101]->NotButton.PhysicalMax = 0
+pp_data->cap[101]->Units = 0
+pp_data->cap[101]->UnitsExp = 0
+
+pp_data->cap[102]->UsagePage = 0xFF01
+pp_data->cap[102]->ReportID = 0x80
+pp_data->cap[102]->BitPosition = 0
+pp_data->cap[102]->BitSize = 8
+pp_data->cap[102]->ReportCount = 1
+pp_data->cap[102]->BytePosition = 0x004F
+pp_data->cap[102]->BitCount = 8
+pp_data->cap[102]->BitField = 0x02
+pp_data->cap[102]->NextBytePosition = 0x0050
+pp_data->cap[102]->LinkCollection = 0x0003
+pp_data->cap[102]->LinkUsagePage = 0xFF01
+pp_data->cap[102]->LinkUsage = 0x0080
+pp_data->cap[102]->IsMultipleItemsForArray = 0
+pp_data->cap[102]->IsButtonCap = 0
+pp_data->cap[102]->IsPadding = 0
+pp_data->cap[102]->IsAbsolute = 1
+pp_data->cap[102]->IsRange = 0
+pp_data->cap[102]->IsAlias = 0
+pp_data->cap[102]->IsStringRange = 0
+pp_data->cap[102]->IsDesignatorRange = 0
+pp_data->cap[102]->Reserved1 = 0x000000
+pp_data->cap[102]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[102]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[102]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[102]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[102]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[102]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[102]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[102]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[102]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[102]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[102]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[102]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[102]->NotRange.Usage = 0x0081
+pp_data->cap[102]->NotRange.Reserved1 = 0x0081
+pp_data->cap[102]->NotRange.StringIndex = 0
+pp_data->cap[102]->NotRange.Reserved2 = 0
+pp_data->cap[102]->NotRange.DesignatorIndex = 0
+pp_data->cap[102]->NotRange.Reserved3 = 0
+pp_data->cap[102]->NotRange.DataIndex = 15
+pp_data->cap[102]->NotRange.Reserved4 = 15
+pp_data->cap[102]->NotButton.HasNull = 0
+pp_data->cap[102]->NotButton.Reserved4 = 0x000000
+pp_data->cap[102]->NotButton.LogicalMin = 0
+pp_data->cap[102]->NotButton.LogicalMax = 127
+pp_data->cap[102]->NotButton.PhysicalMin = 0
+pp_data->cap[102]->NotButton.PhysicalMax = 0
+pp_data->cap[102]->Units = 0
+pp_data->cap[102]->UnitsExp = 0
+
+pp_data->cap[103]->UsagePage = 0xFF01
+pp_data->cap[103]->ReportID = 0x80
+pp_data->cap[103]->BitPosition = 0
+pp_data->cap[103]->BitSize = 8
+pp_data->cap[103]->ReportCount = 1
+pp_data->cap[103]->BytePosition = 0x004E
+pp_data->cap[103]->BitCount = 8
+pp_data->cap[103]->BitField = 0x02
+pp_data->cap[103]->NextBytePosition = 0x004F
+pp_data->cap[103]->LinkCollection = 0x0003
+pp_data->cap[103]->LinkUsagePage = 0xFF01
+pp_data->cap[103]->LinkUsage = 0x0080
+pp_data->cap[103]->IsMultipleItemsForArray = 0
+pp_data->cap[103]->IsButtonCap = 0
+pp_data->cap[103]->IsPadding = 0
+pp_data->cap[103]->IsAbsolute = 1
+pp_data->cap[103]->IsRange = 0
+pp_data->cap[103]->IsAlias = 0
+pp_data->cap[103]->IsStringRange = 0
+pp_data->cap[103]->IsDesignatorRange = 0
+pp_data->cap[103]->Reserved1 = 0x000000
+pp_data->cap[103]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[103]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[103]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[103]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[103]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[103]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[103]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[103]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[103]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[103]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[103]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[103]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[103]->NotRange.Usage = 0x0081
+pp_data->cap[103]->NotRange.Reserved1 = 0x0081
+pp_data->cap[103]->NotRange.StringIndex = 0
+pp_data->cap[103]->NotRange.Reserved2 = 0
+pp_data->cap[103]->NotRange.DesignatorIndex = 0
+pp_data->cap[103]->NotRange.Reserved3 = 0
+pp_data->cap[103]->NotRange.DataIndex = 16
+pp_data->cap[103]->NotRange.Reserved4 = 16
+pp_data->cap[103]->NotButton.HasNull = 0
+pp_data->cap[103]->NotButton.Reserved4 = 0x000000
+pp_data->cap[103]->NotButton.LogicalMin = 0
+pp_data->cap[103]->NotButton.LogicalMax = 127
+pp_data->cap[103]->NotButton.PhysicalMin = 0
+pp_data->cap[103]->NotButton.PhysicalMax = 0
+pp_data->cap[103]->Units = 0
+pp_data->cap[103]->UnitsExp = 0
+
+pp_data->cap[104]->UsagePage = 0xFF01
+pp_data->cap[104]->ReportID = 0x80
+pp_data->cap[104]->BitPosition = 0
+pp_data->cap[104]->BitSize = 8
+pp_data->cap[104]->ReportCount = 1
+pp_data->cap[104]->BytePosition = 0x004D
+pp_data->cap[104]->BitCount = 8
+pp_data->cap[104]->BitField = 0x02
+pp_data->cap[104]->NextBytePosition = 0x004E
+pp_data->cap[104]->LinkCollection = 0x0003
+pp_data->cap[104]->LinkUsagePage = 0xFF01
+pp_data->cap[104]->LinkUsage = 0x0080
+pp_data->cap[104]->IsMultipleItemsForArray = 0
+pp_data->cap[104]->IsButtonCap = 0
+pp_data->cap[104]->IsPadding = 0
+pp_data->cap[104]->IsAbsolute = 1
+pp_data->cap[104]->IsRange = 0
+pp_data->cap[104]->IsAlias = 0
+pp_data->cap[104]->IsStringRange = 0
+pp_data->cap[104]->IsDesignatorRange = 0
+pp_data->cap[104]->Reserved1 = 0x000000
+pp_data->cap[104]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[104]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[104]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[104]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[104]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[104]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[104]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[104]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[104]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[104]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[104]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[104]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[104]->NotRange.Usage = 0x0081
+pp_data->cap[104]->NotRange.Reserved1 = 0x0081
+pp_data->cap[104]->NotRange.StringIndex = 0
+pp_data->cap[104]->NotRange.Reserved2 = 0
+pp_data->cap[104]->NotRange.DesignatorIndex = 0
+pp_data->cap[104]->NotRange.Reserved3 = 0
+pp_data->cap[104]->NotRange.DataIndex = 17
+pp_data->cap[104]->NotRange.Reserved4 = 17
+pp_data->cap[104]->NotButton.HasNull = 0
+pp_data->cap[104]->NotButton.Reserved4 = 0x000000
+pp_data->cap[104]->NotButton.LogicalMin = 0
+pp_data->cap[104]->NotButton.LogicalMax = 127
+pp_data->cap[104]->NotButton.PhysicalMin = 0
+pp_data->cap[104]->NotButton.PhysicalMax = 0
+pp_data->cap[104]->Units = 0
+pp_data->cap[104]->UnitsExp = 0
+
+pp_data->cap[105]->UsagePage = 0xFF01
+pp_data->cap[105]->ReportID = 0x80
+pp_data->cap[105]->BitPosition = 0
+pp_data->cap[105]->BitSize = 8
+pp_data->cap[105]->ReportCount = 1
+pp_data->cap[105]->BytePosition = 0x004C
+pp_data->cap[105]->BitCount = 8
+pp_data->cap[105]->BitField = 0x02
+pp_data->cap[105]->NextBytePosition = 0x004D
+pp_data->cap[105]->LinkCollection = 0x0003
+pp_data->cap[105]->LinkUsagePage = 0xFF01
+pp_data->cap[105]->LinkUsage = 0x0080
+pp_data->cap[105]->IsMultipleItemsForArray = 0
+pp_data->cap[105]->IsButtonCap = 0
+pp_data->cap[105]->IsPadding = 0
+pp_data->cap[105]->IsAbsolute = 1
+pp_data->cap[105]->IsRange = 0
+pp_data->cap[105]->IsAlias = 0
+pp_data->cap[105]->IsStringRange = 0
+pp_data->cap[105]->IsDesignatorRange = 0
+pp_data->cap[105]->Reserved1 = 0x000000
+pp_data->cap[105]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[105]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[105]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[105]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[105]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[105]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[105]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[105]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[105]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[105]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[105]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[105]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[105]->NotRange.Usage = 0x0081
+pp_data->cap[105]->NotRange.Reserved1 = 0x0081
+pp_data->cap[105]->NotRange.StringIndex = 0
+pp_data->cap[105]->NotRange.Reserved2 = 0
+pp_data->cap[105]->NotRange.DesignatorIndex = 0
+pp_data->cap[105]->NotRange.Reserved3 = 0
+pp_data->cap[105]->NotRange.DataIndex = 18
+pp_data->cap[105]->NotRange.Reserved4 = 18
+pp_data->cap[105]->NotButton.HasNull = 0
+pp_data->cap[105]->NotButton.Reserved4 = 0x000000
+pp_data->cap[105]->NotButton.LogicalMin = 0
+pp_data->cap[105]->NotButton.LogicalMax = 127
+pp_data->cap[105]->NotButton.PhysicalMin = 0
+pp_data->cap[105]->NotButton.PhysicalMax = 0
+pp_data->cap[105]->Units = 0
+pp_data->cap[105]->UnitsExp = 0
+
+pp_data->cap[106]->UsagePage = 0xFF01
+pp_data->cap[106]->ReportID = 0x80
+pp_data->cap[106]->BitPosition = 0
+pp_data->cap[106]->BitSize = 8
+pp_data->cap[106]->ReportCount = 1
+pp_data->cap[106]->BytePosition = 0x004B
+pp_data->cap[106]->BitCount = 8
+pp_data->cap[106]->BitField = 0x02
+pp_data->cap[106]->NextBytePosition = 0x004C
+pp_data->cap[106]->LinkCollection = 0x0003
+pp_data->cap[106]->LinkUsagePage = 0xFF01
+pp_data->cap[106]->LinkUsage = 0x0080
+pp_data->cap[106]->IsMultipleItemsForArray = 0
+pp_data->cap[106]->IsButtonCap = 0
+pp_data->cap[106]->IsPadding = 0
+pp_data->cap[106]->IsAbsolute = 1
+pp_data->cap[106]->IsRange = 0
+pp_data->cap[106]->IsAlias = 0
+pp_data->cap[106]->IsStringRange = 0
+pp_data->cap[106]->IsDesignatorRange = 0
+pp_data->cap[106]->Reserved1 = 0x000000
+pp_data->cap[106]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[106]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[106]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[106]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[106]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[106]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[106]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[106]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[106]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[106]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[106]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[106]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[106]->NotRange.Usage = 0x0081
+pp_data->cap[106]->NotRange.Reserved1 = 0x0081
+pp_data->cap[106]->NotRange.StringIndex = 0
+pp_data->cap[106]->NotRange.Reserved2 = 0
+pp_data->cap[106]->NotRange.DesignatorIndex = 0
+pp_data->cap[106]->NotRange.Reserved3 = 0
+pp_data->cap[106]->NotRange.DataIndex = 19
+pp_data->cap[106]->NotRange.Reserved4 = 19
+pp_data->cap[106]->NotButton.HasNull = 0
+pp_data->cap[106]->NotButton.Reserved4 = 0x000000
+pp_data->cap[106]->NotButton.LogicalMin = 0
+pp_data->cap[106]->NotButton.LogicalMax = 127
+pp_data->cap[106]->NotButton.PhysicalMin = 0
+pp_data->cap[106]->NotButton.PhysicalMax = 0
+pp_data->cap[106]->Units = 0
+pp_data->cap[106]->UnitsExp = 0
+
+pp_data->cap[107]->UsagePage = 0xFF01
+pp_data->cap[107]->ReportID = 0x80
+pp_data->cap[107]->BitPosition = 0
+pp_data->cap[107]->BitSize = 8
+pp_data->cap[107]->ReportCount = 1
+pp_data->cap[107]->BytePosition = 0x004A
+pp_data->cap[107]->BitCount = 8
+pp_data->cap[107]->BitField = 0x02
+pp_data->cap[107]->NextBytePosition = 0x004B
+pp_data->cap[107]->LinkCollection = 0x0003
+pp_data->cap[107]->LinkUsagePage = 0xFF01
+pp_data->cap[107]->LinkUsage = 0x0080
+pp_data->cap[107]->IsMultipleItemsForArray = 0
+pp_data->cap[107]->IsButtonCap = 0
+pp_data->cap[107]->IsPadding = 0
+pp_data->cap[107]->IsAbsolute = 1
+pp_data->cap[107]->IsRange = 0
+pp_data->cap[107]->IsAlias = 0
+pp_data->cap[107]->IsStringRange = 0
+pp_data->cap[107]->IsDesignatorRange = 0
+pp_data->cap[107]->Reserved1 = 0x000000
+pp_data->cap[107]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[107]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[107]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[107]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[107]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[107]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[107]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[107]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[107]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[107]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[107]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[107]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[107]->NotRange.Usage = 0x0081
+pp_data->cap[107]->NotRange.Reserved1 = 0x0081
+pp_data->cap[107]->NotRange.StringIndex = 0
+pp_data->cap[107]->NotRange.Reserved2 = 0
+pp_data->cap[107]->NotRange.DesignatorIndex = 0
+pp_data->cap[107]->NotRange.Reserved3 = 0
+pp_data->cap[107]->NotRange.DataIndex = 20
+pp_data->cap[107]->NotRange.Reserved4 = 20
+pp_data->cap[107]->NotButton.HasNull = 0
+pp_data->cap[107]->NotButton.Reserved4 = 0x000000
+pp_data->cap[107]->NotButton.LogicalMin = 0
+pp_data->cap[107]->NotButton.LogicalMax = 127
+pp_data->cap[107]->NotButton.PhysicalMin = 0
+pp_data->cap[107]->NotButton.PhysicalMax = 0
+pp_data->cap[107]->Units = 0
+pp_data->cap[107]->UnitsExp = 0
+
+pp_data->cap[108]->UsagePage = 0xFF01
+pp_data->cap[108]->ReportID = 0x80
+pp_data->cap[108]->BitPosition = 0
+pp_data->cap[108]->BitSize = 8
+pp_data->cap[108]->ReportCount = 1
+pp_data->cap[108]->BytePosition = 0x0049
+pp_data->cap[108]->BitCount = 8
+pp_data->cap[108]->BitField = 0x02
+pp_data->cap[108]->NextBytePosition = 0x004A
+pp_data->cap[108]->LinkCollection = 0x0003
+pp_data->cap[108]->LinkUsagePage = 0xFF01
+pp_data->cap[108]->LinkUsage = 0x0080
+pp_data->cap[108]->IsMultipleItemsForArray = 0
+pp_data->cap[108]->IsButtonCap = 0
+pp_data->cap[108]->IsPadding = 0
+pp_data->cap[108]->IsAbsolute = 1
+pp_data->cap[108]->IsRange = 0
+pp_data->cap[108]->IsAlias = 0
+pp_data->cap[108]->IsStringRange = 0
+pp_data->cap[108]->IsDesignatorRange = 0
+pp_data->cap[108]->Reserved1 = 0x000000
+pp_data->cap[108]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[108]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[108]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[108]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[108]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[108]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[108]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[108]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[108]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[108]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[108]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[108]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[108]->NotRange.Usage = 0x0081
+pp_data->cap[108]->NotRange.Reserved1 = 0x0081
+pp_data->cap[108]->NotRange.StringIndex = 0
+pp_data->cap[108]->NotRange.Reserved2 = 0
+pp_data->cap[108]->NotRange.DesignatorIndex = 0
+pp_data->cap[108]->NotRange.Reserved3 = 0
+pp_data->cap[108]->NotRange.DataIndex = 21
+pp_data->cap[108]->NotRange.Reserved4 = 21
+pp_data->cap[108]->NotButton.HasNull = 0
+pp_data->cap[108]->NotButton.Reserved4 = 0x000000
+pp_data->cap[108]->NotButton.LogicalMin = 0
+pp_data->cap[108]->NotButton.LogicalMax = 127
+pp_data->cap[108]->NotButton.PhysicalMin = 0
+pp_data->cap[108]->NotButton.PhysicalMax = 0
+pp_data->cap[108]->Units = 0
+pp_data->cap[108]->UnitsExp = 0
+
+pp_data->cap[109]->UsagePage = 0xFF01
+pp_data->cap[109]->ReportID = 0x80
+pp_data->cap[109]->BitPosition = 0
+pp_data->cap[109]->BitSize = 8
+pp_data->cap[109]->ReportCount = 1
+pp_data->cap[109]->BytePosition = 0x0048
+pp_data->cap[109]->BitCount = 8
+pp_data->cap[109]->BitField = 0x02
+pp_data->cap[109]->NextBytePosition = 0x0049
+pp_data->cap[109]->LinkCollection = 0x0003
+pp_data->cap[109]->LinkUsagePage = 0xFF01
+pp_data->cap[109]->LinkUsage = 0x0080
+pp_data->cap[109]->IsMultipleItemsForArray = 0
+pp_data->cap[109]->IsButtonCap = 0
+pp_data->cap[109]->IsPadding = 0
+pp_data->cap[109]->IsAbsolute = 1
+pp_data->cap[109]->IsRange = 0
+pp_data->cap[109]->IsAlias = 0
+pp_data->cap[109]->IsStringRange = 0
+pp_data->cap[109]->IsDesignatorRange = 0
+pp_data->cap[109]->Reserved1 = 0x000000
+pp_data->cap[109]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[109]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[109]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[109]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[109]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[109]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[109]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[109]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[109]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[109]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[109]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[109]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[109]->NotRange.Usage = 0x0081
+pp_data->cap[109]->NotRange.Reserved1 = 0x0081
+pp_data->cap[109]->NotRange.StringIndex = 0
+pp_data->cap[109]->NotRange.Reserved2 = 0
+pp_data->cap[109]->NotRange.DesignatorIndex = 0
+pp_data->cap[109]->NotRange.Reserved3 = 0
+pp_data->cap[109]->NotRange.DataIndex = 22
+pp_data->cap[109]->NotRange.Reserved4 = 22
+pp_data->cap[109]->NotButton.HasNull = 0
+pp_data->cap[109]->NotButton.Reserved4 = 0x000000
+pp_data->cap[109]->NotButton.LogicalMin = 0
+pp_data->cap[109]->NotButton.LogicalMax = 127
+pp_data->cap[109]->NotButton.PhysicalMin = 0
+pp_data->cap[109]->NotButton.PhysicalMax = 0
+pp_data->cap[109]->Units = 0
+pp_data->cap[109]->UnitsExp = 0
+
+pp_data->cap[110]->UsagePage = 0xFF01
+pp_data->cap[110]->ReportID = 0x80
+pp_data->cap[110]->BitPosition = 0
+pp_data->cap[110]->BitSize = 8
+pp_data->cap[110]->ReportCount = 1
+pp_data->cap[110]->BytePosition = 0x0047
+pp_data->cap[110]->BitCount = 8
+pp_data->cap[110]->BitField = 0x02
+pp_data->cap[110]->NextBytePosition = 0x0048
+pp_data->cap[110]->LinkCollection = 0x0003
+pp_data->cap[110]->LinkUsagePage = 0xFF01
+pp_data->cap[110]->LinkUsage = 0x0080
+pp_data->cap[110]->IsMultipleItemsForArray = 0
+pp_data->cap[110]->IsButtonCap = 0
+pp_data->cap[110]->IsPadding = 0
+pp_data->cap[110]->IsAbsolute = 1
+pp_data->cap[110]->IsRange = 0
+pp_data->cap[110]->IsAlias = 0
+pp_data->cap[110]->IsStringRange = 0
+pp_data->cap[110]->IsDesignatorRange = 0
+pp_data->cap[110]->Reserved1 = 0x000000
+pp_data->cap[110]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[110]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[110]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[110]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[110]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[110]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[110]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[110]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[110]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[110]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[110]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[110]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[110]->NotRange.Usage = 0x0081
+pp_data->cap[110]->NotRange.Reserved1 = 0x0081
+pp_data->cap[110]->NotRange.StringIndex = 0
+pp_data->cap[110]->NotRange.Reserved2 = 0
+pp_data->cap[110]->NotRange.DesignatorIndex = 0
+pp_data->cap[110]->NotRange.Reserved3 = 0
+pp_data->cap[110]->NotRange.DataIndex = 23
+pp_data->cap[110]->NotRange.Reserved4 = 23
+pp_data->cap[110]->NotButton.HasNull = 0
+pp_data->cap[110]->NotButton.Reserved4 = 0x000000
+pp_data->cap[110]->NotButton.LogicalMin = 0
+pp_data->cap[110]->NotButton.LogicalMax = 127
+pp_data->cap[110]->NotButton.PhysicalMin = 0
+pp_data->cap[110]->NotButton.PhysicalMax = 0
+pp_data->cap[110]->Units = 0
+pp_data->cap[110]->UnitsExp = 0
+
+pp_data->cap[111]->UsagePage = 0xFF01
+pp_data->cap[111]->ReportID = 0x80
+pp_data->cap[111]->BitPosition = 0
+pp_data->cap[111]->BitSize = 8
+pp_data->cap[111]->ReportCount = 1
+pp_data->cap[111]->BytePosition = 0x0046
+pp_data->cap[111]->BitCount = 8
+pp_data->cap[111]->BitField = 0x02
+pp_data->cap[111]->NextBytePosition = 0x0047
+pp_data->cap[111]->LinkCollection = 0x0003
+pp_data->cap[111]->LinkUsagePage = 0xFF01
+pp_data->cap[111]->LinkUsage = 0x0080
+pp_data->cap[111]->IsMultipleItemsForArray = 0
+pp_data->cap[111]->IsButtonCap = 0
+pp_data->cap[111]->IsPadding = 0
+pp_data->cap[111]->IsAbsolute = 1
+pp_data->cap[111]->IsRange = 0
+pp_data->cap[111]->IsAlias = 0
+pp_data->cap[111]->IsStringRange = 0
+pp_data->cap[111]->IsDesignatorRange = 0
+pp_data->cap[111]->Reserved1 = 0x000000
+pp_data->cap[111]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[111]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[111]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[111]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[111]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[111]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[111]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[111]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[111]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[111]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[111]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[111]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[111]->NotRange.Usage = 0x0081
+pp_data->cap[111]->NotRange.Reserved1 = 0x0081
+pp_data->cap[111]->NotRange.StringIndex = 0
+pp_data->cap[111]->NotRange.Reserved2 = 0
+pp_data->cap[111]->NotRange.DesignatorIndex = 0
+pp_data->cap[111]->NotRange.Reserved3 = 0
+pp_data->cap[111]->NotRange.DataIndex = 24
+pp_data->cap[111]->NotRange.Reserved4 = 24
+pp_data->cap[111]->NotButton.HasNull = 0
+pp_data->cap[111]->NotButton.Reserved4 = 0x000000
+pp_data->cap[111]->NotButton.LogicalMin = 0
+pp_data->cap[111]->NotButton.LogicalMax = 127
+pp_data->cap[111]->NotButton.PhysicalMin = 0
+pp_data->cap[111]->NotButton.PhysicalMax = 0
+pp_data->cap[111]->Units = 0
+pp_data->cap[111]->UnitsExp = 0
+
+pp_data->cap[112]->UsagePage = 0xFF01
+pp_data->cap[112]->ReportID = 0x80
+pp_data->cap[112]->BitPosition = 0
+pp_data->cap[112]->BitSize = 8
+pp_data->cap[112]->ReportCount = 1
+pp_data->cap[112]->BytePosition = 0x0045
+pp_data->cap[112]->BitCount = 8
+pp_data->cap[112]->BitField = 0x02
+pp_data->cap[112]->NextBytePosition = 0x0046
+pp_data->cap[112]->LinkCollection = 0x0003
+pp_data->cap[112]->LinkUsagePage = 0xFF01
+pp_data->cap[112]->LinkUsage = 0x0080
+pp_data->cap[112]->IsMultipleItemsForArray = 0
+pp_data->cap[112]->IsButtonCap = 0
+pp_data->cap[112]->IsPadding = 0
+pp_data->cap[112]->IsAbsolute = 1
+pp_data->cap[112]->IsRange = 0
+pp_data->cap[112]->IsAlias = 0
+pp_data->cap[112]->IsStringRange = 0
+pp_data->cap[112]->IsDesignatorRange = 0
+pp_data->cap[112]->Reserved1 = 0x000000
+pp_data->cap[112]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[112]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[112]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[112]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[112]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[112]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[112]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[112]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[112]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[112]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[112]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[112]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[112]->NotRange.Usage = 0x0081
+pp_data->cap[112]->NotRange.Reserved1 = 0x0081
+pp_data->cap[112]->NotRange.StringIndex = 0
+pp_data->cap[112]->NotRange.Reserved2 = 0
+pp_data->cap[112]->NotRange.DesignatorIndex = 0
+pp_data->cap[112]->NotRange.Reserved3 = 0
+pp_data->cap[112]->NotRange.DataIndex = 25
+pp_data->cap[112]->NotRange.Reserved4 = 25
+pp_data->cap[112]->NotButton.HasNull = 0
+pp_data->cap[112]->NotButton.Reserved4 = 0x000000
+pp_data->cap[112]->NotButton.LogicalMin = 0
+pp_data->cap[112]->NotButton.LogicalMax = 127
+pp_data->cap[112]->NotButton.PhysicalMin = 0
+pp_data->cap[112]->NotButton.PhysicalMax = 0
+pp_data->cap[112]->Units = 0
+pp_data->cap[112]->UnitsExp = 0
+
+pp_data->cap[113]->UsagePage = 0xFF01
+pp_data->cap[113]->ReportID = 0x80
+pp_data->cap[113]->BitPosition = 0
+pp_data->cap[113]->BitSize = 8
+pp_data->cap[113]->ReportCount = 1
+pp_data->cap[113]->BytePosition = 0x0044
+pp_data->cap[113]->BitCount = 8
+pp_data->cap[113]->BitField = 0x02
+pp_data->cap[113]->NextBytePosition = 0x0045
+pp_data->cap[113]->LinkCollection = 0x0003
+pp_data->cap[113]->LinkUsagePage = 0xFF01
+pp_data->cap[113]->LinkUsage = 0x0080
+pp_data->cap[113]->IsMultipleItemsForArray = 0
+pp_data->cap[113]->IsButtonCap = 0
+pp_data->cap[113]->IsPadding = 0
+pp_data->cap[113]->IsAbsolute = 1
+pp_data->cap[113]->IsRange = 0
+pp_data->cap[113]->IsAlias = 0
+pp_data->cap[113]->IsStringRange = 0
+pp_data->cap[113]->IsDesignatorRange = 0
+pp_data->cap[113]->Reserved1 = 0x000000
+pp_data->cap[113]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[113]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[113]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[113]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[113]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[113]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[113]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[113]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[113]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[113]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[113]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[113]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[113]->NotRange.Usage = 0x0081
+pp_data->cap[113]->NotRange.Reserved1 = 0x0081
+pp_data->cap[113]->NotRange.StringIndex = 0
+pp_data->cap[113]->NotRange.Reserved2 = 0
+pp_data->cap[113]->NotRange.DesignatorIndex = 0
+pp_data->cap[113]->NotRange.Reserved3 = 0
+pp_data->cap[113]->NotRange.DataIndex = 26
+pp_data->cap[113]->NotRange.Reserved4 = 26
+pp_data->cap[113]->NotButton.HasNull = 0
+pp_data->cap[113]->NotButton.Reserved4 = 0x000000
+pp_data->cap[113]->NotButton.LogicalMin = 0
+pp_data->cap[113]->NotButton.LogicalMax = 127
+pp_data->cap[113]->NotButton.PhysicalMin = 0
+pp_data->cap[113]->NotButton.PhysicalMax = 0
+pp_data->cap[113]->Units = 0
+pp_data->cap[113]->UnitsExp = 0
+
+pp_data->cap[114]->UsagePage = 0xFF01
+pp_data->cap[114]->ReportID = 0x80
+pp_data->cap[114]->BitPosition = 0
+pp_data->cap[114]->BitSize = 8
+pp_data->cap[114]->ReportCount = 1
+pp_data->cap[114]->BytePosition = 0x0043
+pp_data->cap[114]->BitCount = 8
+pp_data->cap[114]->BitField = 0x02
+pp_data->cap[114]->NextBytePosition = 0x0044
+pp_data->cap[114]->LinkCollection = 0x0003
+pp_data->cap[114]->LinkUsagePage = 0xFF01
+pp_data->cap[114]->LinkUsage = 0x0080
+pp_data->cap[114]->IsMultipleItemsForArray = 0
+pp_data->cap[114]->IsButtonCap = 0
+pp_data->cap[114]->IsPadding = 0
+pp_data->cap[114]->IsAbsolute = 1
+pp_data->cap[114]->IsRange = 0
+pp_data->cap[114]->IsAlias = 0
+pp_data->cap[114]->IsStringRange = 0
+pp_data->cap[114]->IsDesignatorRange = 0
+pp_data->cap[114]->Reserved1 = 0x000000
+pp_data->cap[114]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[114]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[114]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[114]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[114]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[114]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[114]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[114]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[114]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[114]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[114]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[114]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[114]->NotRange.Usage = 0x0081
+pp_data->cap[114]->NotRange.Reserved1 = 0x0081
+pp_data->cap[114]->NotRange.StringIndex = 0
+pp_data->cap[114]->NotRange.Reserved2 = 0
+pp_data->cap[114]->NotRange.DesignatorIndex = 0
+pp_data->cap[114]->NotRange.Reserved3 = 0
+pp_data->cap[114]->NotRange.DataIndex = 27
+pp_data->cap[114]->NotRange.Reserved4 = 27
+pp_data->cap[114]->NotButton.HasNull = 0
+pp_data->cap[114]->NotButton.Reserved4 = 0x000000
+pp_data->cap[114]->NotButton.LogicalMin = 0
+pp_data->cap[114]->NotButton.LogicalMax = 127
+pp_data->cap[114]->NotButton.PhysicalMin = 0
+pp_data->cap[114]->NotButton.PhysicalMax = 0
+pp_data->cap[114]->Units = 0
+pp_data->cap[114]->UnitsExp = 0
+
+pp_data->cap[115]->UsagePage = 0xFF01
+pp_data->cap[115]->ReportID = 0x80
+pp_data->cap[115]->BitPosition = 0
+pp_data->cap[115]->BitSize = 8
+pp_data->cap[115]->ReportCount = 1
+pp_data->cap[115]->BytePosition = 0x0042
+pp_data->cap[115]->BitCount = 8
+pp_data->cap[115]->BitField = 0x02
+pp_data->cap[115]->NextBytePosition = 0x0043
+pp_data->cap[115]->LinkCollection = 0x0003
+pp_data->cap[115]->LinkUsagePage = 0xFF01
+pp_data->cap[115]->LinkUsage = 0x0080
+pp_data->cap[115]->IsMultipleItemsForArray = 0
+pp_data->cap[115]->IsButtonCap = 0
+pp_data->cap[115]->IsPadding = 0
+pp_data->cap[115]->IsAbsolute = 1
+pp_data->cap[115]->IsRange = 0
+pp_data->cap[115]->IsAlias = 0
+pp_data->cap[115]->IsStringRange = 0
+pp_data->cap[115]->IsDesignatorRange = 0
+pp_data->cap[115]->Reserved1 = 0x000000
+pp_data->cap[115]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[115]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[115]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[115]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[115]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[115]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[115]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[115]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[115]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[115]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[115]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[115]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[115]->NotRange.Usage = 0x0081
+pp_data->cap[115]->NotRange.Reserved1 = 0x0081
+pp_data->cap[115]->NotRange.StringIndex = 0
+pp_data->cap[115]->NotRange.Reserved2 = 0
+pp_data->cap[115]->NotRange.DesignatorIndex = 0
+pp_data->cap[115]->NotRange.Reserved3 = 0
+pp_data->cap[115]->NotRange.DataIndex = 28
+pp_data->cap[115]->NotRange.Reserved4 = 28
+pp_data->cap[115]->NotButton.HasNull = 0
+pp_data->cap[115]->NotButton.Reserved4 = 0x000000
+pp_data->cap[115]->NotButton.LogicalMin = 0
+pp_data->cap[115]->NotButton.LogicalMax = 127
+pp_data->cap[115]->NotButton.PhysicalMin = 0
+pp_data->cap[115]->NotButton.PhysicalMax = 0
+pp_data->cap[115]->Units = 0
+pp_data->cap[115]->UnitsExp = 0
+
+pp_data->cap[116]->UsagePage = 0xFF01
+pp_data->cap[116]->ReportID = 0x80
+pp_data->cap[116]->BitPosition = 0
+pp_data->cap[116]->BitSize = 8
+pp_data->cap[116]->ReportCount = 1
+pp_data->cap[116]->BytePosition = 0x0041
+pp_data->cap[116]->BitCount = 8
+pp_data->cap[116]->BitField = 0x02
+pp_data->cap[116]->NextBytePosition = 0x0042
+pp_data->cap[116]->LinkCollection = 0x0003
+pp_data->cap[116]->LinkUsagePage = 0xFF01
+pp_data->cap[116]->LinkUsage = 0x0080
+pp_data->cap[116]->IsMultipleItemsForArray = 0
+pp_data->cap[116]->IsButtonCap = 0
+pp_data->cap[116]->IsPadding = 0
+pp_data->cap[116]->IsAbsolute = 1
+pp_data->cap[116]->IsRange = 0
+pp_data->cap[116]->IsAlias = 0
+pp_data->cap[116]->IsStringRange = 0
+pp_data->cap[116]->IsDesignatorRange = 0
+pp_data->cap[116]->Reserved1 = 0x000000
+pp_data->cap[116]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[116]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[116]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[116]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[116]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[116]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[116]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[116]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[116]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[116]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[116]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[116]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[116]->NotRange.Usage = 0x0081
+pp_data->cap[116]->NotRange.Reserved1 = 0x0081
+pp_data->cap[116]->NotRange.StringIndex = 0
+pp_data->cap[116]->NotRange.Reserved2 = 0
+pp_data->cap[116]->NotRange.DesignatorIndex = 0
+pp_data->cap[116]->NotRange.Reserved3 = 0
+pp_data->cap[116]->NotRange.DataIndex = 29
+pp_data->cap[116]->NotRange.Reserved4 = 29
+pp_data->cap[116]->NotButton.HasNull = 0
+pp_data->cap[116]->NotButton.Reserved4 = 0x000000
+pp_data->cap[116]->NotButton.LogicalMin = 0
+pp_data->cap[116]->NotButton.LogicalMax = 127
+pp_data->cap[116]->NotButton.PhysicalMin = 0
+pp_data->cap[116]->NotButton.PhysicalMax = 0
+pp_data->cap[116]->Units = 0
+pp_data->cap[116]->UnitsExp = 0
+
+pp_data->cap[117]->UsagePage = 0xFF01
+pp_data->cap[117]->ReportID = 0x80
+pp_data->cap[117]->BitPosition = 0
+pp_data->cap[117]->BitSize = 8
+pp_data->cap[117]->ReportCount = 1
+pp_data->cap[117]->BytePosition = 0x0040
+pp_data->cap[117]->BitCount = 8
+pp_data->cap[117]->BitField = 0x02
+pp_data->cap[117]->NextBytePosition = 0x0041
+pp_data->cap[117]->LinkCollection = 0x0003
+pp_data->cap[117]->LinkUsagePage = 0xFF01
+pp_data->cap[117]->LinkUsage = 0x0080
+pp_data->cap[117]->IsMultipleItemsForArray = 0
+pp_data->cap[117]->IsButtonCap = 0
+pp_data->cap[117]->IsPadding = 0
+pp_data->cap[117]->IsAbsolute = 1
+pp_data->cap[117]->IsRange = 0
+pp_data->cap[117]->IsAlias = 0
+pp_data->cap[117]->IsStringRange = 0
+pp_data->cap[117]->IsDesignatorRange = 0
+pp_data->cap[117]->Reserved1 = 0x000000
+pp_data->cap[117]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[117]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[117]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[117]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[117]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[117]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[117]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[117]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[117]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[117]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[117]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[117]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[117]->NotRange.Usage = 0x0081
+pp_data->cap[117]->NotRange.Reserved1 = 0x0081
+pp_data->cap[117]->NotRange.StringIndex = 0
+pp_data->cap[117]->NotRange.Reserved2 = 0
+pp_data->cap[117]->NotRange.DesignatorIndex = 0
+pp_data->cap[117]->NotRange.Reserved3 = 0
+pp_data->cap[117]->NotRange.DataIndex = 30
+pp_data->cap[117]->NotRange.Reserved4 = 30
+pp_data->cap[117]->NotButton.HasNull = 0
+pp_data->cap[117]->NotButton.Reserved4 = 0x000000
+pp_data->cap[117]->NotButton.LogicalMin = 0
+pp_data->cap[117]->NotButton.LogicalMax = 127
+pp_data->cap[117]->NotButton.PhysicalMin = 0
+pp_data->cap[117]->NotButton.PhysicalMax = 0
+pp_data->cap[117]->Units = 0
+pp_data->cap[117]->UnitsExp = 0
+
+pp_data->cap[118]->UsagePage = 0xFF01
+pp_data->cap[118]->ReportID = 0x80
+pp_data->cap[118]->BitPosition = 0
+pp_data->cap[118]->BitSize = 8
+pp_data->cap[118]->ReportCount = 1
+pp_data->cap[118]->BytePosition = 0x003F
+pp_data->cap[118]->BitCount = 8
+pp_data->cap[118]->BitField = 0x02
+pp_data->cap[118]->NextBytePosition = 0x0040
+pp_data->cap[118]->LinkCollection = 0x0003
+pp_data->cap[118]->LinkUsagePage = 0xFF01
+pp_data->cap[118]->LinkUsage = 0x0080
+pp_data->cap[118]->IsMultipleItemsForArray = 0
+pp_data->cap[118]->IsButtonCap = 0
+pp_data->cap[118]->IsPadding = 0
+pp_data->cap[118]->IsAbsolute = 1
+pp_data->cap[118]->IsRange = 0
+pp_data->cap[118]->IsAlias = 0
+pp_data->cap[118]->IsStringRange = 0
+pp_data->cap[118]->IsDesignatorRange = 0
+pp_data->cap[118]->Reserved1 = 0x000000
+pp_data->cap[118]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[118]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[118]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[118]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[118]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[118]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[118]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[118]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[118]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[118]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[118]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[118]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[118]->NotRange.Usage = 0x0081
+pp_data->cap[118]->NotRange.Reserved1 = 0x0081
+pp_data->cap[118]->NotRange.StringIndex = 0
+pp_data->cap[118]->NotRange.Reserved2 = 0
+pp_data->cap[118]->NotRange.DesignatorIndex = 0
+pp_data->cap[118]->NotRange.Reserved3 = 0
+pp_data->cap[118]->NotRange.DataIndex = 31
+pp_data->cap[118]->NotRange.Reserved4 = 31
+pp_data->cap[118]->NotButton.HasNull = 0
+pp_data->cap[118]->NotButton.Reserved4 = 0x000000
+pp_data->cap[118]->NotButton.LogicalMin = 0
+pp_data->cap[118]->NotButton.LogicalMax = 127
+pp_data->cap[118]->NotButton.PhysicalMin = 0
+pp_data->cap[118]->NotButton.PhysicalMax = 0
+pp_data->cap[118]->Units = 0
+pp_data->cap[118]->UnitsExp = 0
+
+pp_data->cap[119]->UsagePage = 0xFF01
+pp_data->cap[119]->ReportID = 0x80
+pp_data->cap[119]->BitPosition = 0
+pp_data->cap[119]->BitSize = 8
+pp_data->cap[119]->ReportCount = 1
+pp_data->cap[119]->BytePosition = 0x003E
+pp_data->cap[119]->BitCount = 8
+pp_data->cap[119]->BitField = 0x02
+pp_data->cap[119]->NextBytePosition = 0x003F
+pp_data->cap[119]->LinkCollection = 0x0003
+pp_data->cap[119]->LinkUsagePage = 0xFF01
+pp_data->cap[119]->LinkUsage = 0x0080
+pp_data->cap[119]->IsMultipleItemsForArray = 0
+pp_data->cap[119]->IsButtonCap = 0
+pp_data->cap[119]->IsPadding = 0
+pp_data->cap[119]->IsAbsolute = 1
+pp_data->cap[119]->IsRange = 0
+pp_data->cap[119]->IsAlias = 0
+pp_data->cap[119]->IsStringRange = 0
+pp_data->cap[119]->IsDesignatorRange = 0
+pp_data->cap[119]->Reserved1 = 0x000000
+pp_data->cap[119]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[119]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[119]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[119]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[119]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[119]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[119]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[119]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[119]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[119]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[119]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[119]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[119]->NotRange.Usage = 0x0081
+pp_data->cap[119]->NotRange.Reserved1 = 0x0081
+pp_data->cap[119]->NotRange.StringIndex = 0
+pp_data->cap[119]->NotRange.Reserved2 = 0
+pp_data->cap[119]->NotRange.DesignatorIndex = 0
+pp_data->cap[119]->NotRange.Reserved3 = 0
+pp_data->cap[119]->NotRange.DataIndex = 32
+pp_data->cap[119]->NotRange.Reserved4 = 32
+pp_data->cap[119]->NotButton.HasNull = 0
+pp_data->cap[119]->NotButton.Reserved4 = 0x000000
+pp_data->cap[119]->NotButton.LogicalMin = 0
+pp_data->cap[119]->NotButton.LogicalMax = 127
+pp_data->cap[119]->NotButton.PhysicalMin = 0
+pp_data->cap[119]->NotButton.PhysicalMax = 0
+pp_data->cap[119]->Units = 0
+pp_data->cap[119]->UnitsExp = 0
+
+pp_data->cap[120]->UsagePage = 0xFF01
+pp_data->cap[120]->ReportID = 0x80
+pp_data->cap[120]->BitPosition = 0
+pp_data->cap[120]->BitSize = 8
+pp_data->cap[120]->ReportCount = 1
+pp_data->cap[120]->BytePosition = 0x003D
+pp_data->cap[120]->BitCount = 8
+pp_data->cap[120]->BitField = 0x02
+pp_data->cap[120]->NextBytePosition = 0x003E
+pp_data->cap[120]->LinkCollection = 0x0003
+pp_data->cap[120]->LinkUsagePage = 0xFF01
+pp_data->cap[120]->LinkUsage = 0x0080
+pp_data->cap[120]->IsMultipleItemsForArray = 0
+pp_data->cap[120]->IsButtonCap = 0
+pp_data->cap[120]->IsPadding = 0
+pp_data->cap[120]->IsAbsolute = 1
+pp_data->cap[120]->IsRange = 0
+pp_data->cap[120]->IsAlias = 0
+pp_data->cap[120]->IsStringRange = 0
+pp_data->cap[120]->IsDesignatorRange = 0
+pp_data->cap[120]->Reserved1 = 0x000000
+pp_data->cap[120]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[120]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[120]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[120]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[120]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[120]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[120]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[120]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[120]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[120]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[120]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[120]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[120]->NotRange.Usage = 0x0081
+pp_data->cap[120]->NotRange.Reserved1 = 0x0081
+pp_data->cap[120]->NotRange.StringIndex = 0
+pp_data->cap[120]->NotRange.Reserved2 = 0
+pp_data->cap[120]->NotRange.DesignatorIndex = 0
+pp_data->cap[120]->NotRange.Reserved3 = 0
+pp_data->cap[120]->NotRange.DataIndex = 33
+pp_data->cap[120]->NotRange.Reserved4 = 33
+pp_data->cap[120]->NotButton.HasNull = 0
+pp_data->cap[120]->NotButton.Reserved4 = 0x000000
+pp_data->cap[120]->NotButton.LogicalMin = 0
+pp_data->cap[120]->NotButton.LogicalMax = 127
+pp_data->cap[120]->NotButton.PhysicalMin = 0
+pp_data->cap[120]->NotButton.PhysicalMax = 0
+pp_data->cap[120]->Units = 0
+pp_data->cap[120]->UnitsExp = 0
+
+pp_data->cap[121]->UsagePage = 0xFF01
+pp_data->cap[121]->ReportID = 0x80
+pp_data->cap[121]->BitPosition = 0
+pp_data->cap[121]->BitSize = 8
+pp_data->cap[121]->ReportCount = 1
+pp_data->cap[121]->BytePosition = 0x003C
+pp_data->cap[121]->BitCount = 8
+pp_data->cap[121]->BitField = 0x02
+pp_data->cap[121]->NextBytePosition = 0x003D
+pp_data->cap[121]->LinkCollection = 0x0003
+pp_data->cap[121]->LinkUsagePage = 0xFF01
+pp_data->cap[121]->LinkUsage = 0x0080
+pp_data->cap[121]->IsMultipleItemsForArray = 0
+pp_data->cap[121]->IsButtonCap = 0
+pp_data->cap[121]->IsPadding = 0
+pp_data->cap[121]->IsAbsolute = 1
+pp_data->cap[121]->IsRange = 0
+pp_data->cap[121]->IsAlias = 0
+pp_data->cap[121]->IsStringRange = 0
+pp_data->cap[121]->IsDesignatorRange = 0
+pp_data->cap[121]->Reserved1 = 0x000000
+pp_data->cap[121]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[121]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[121]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[121]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[121]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[121]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[121]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[121]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[121]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[121]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[121]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[121]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[121]->NotRange.Usage = 0x0081
+pp_data->cap[121]->NotRange.Reserved1 = 0x0081
+pp_data->cap[121]->NotRange.StringIndex = 0
+pp_data->cap[121]->NotRange.Reserved2 = 0
+pp_data->cap[121]->NotRange.DesignatorIndex = 0
+pp_data->cap[121]->NotRange.Reserved3 = 0
+pp_data->cap[121]->NotRange.DataIndex = 34
+pp_data->cap[121]->NotRange.Reserved4 = 34
+pp_data->cap[121]->NotButton.HasNull = 0
+pp_data->cap[121]->NotButton.Reserved4 = 0x000000
+pp_data->cap[121]->NotButton.LogicalMin = 0
+pp_data->cap[121]->NotButton.LogicalMax = 127
+pp_data->cap[121]->NotButton.PhysicalMin = 0
+pp_data->cap[121]->NotButton.PhysicalMax = 0
+pp_data->cap[121]->Units = 0
+pp_data->cap[121]->UnitsExp = 0
+
+pp_data->cap[122]->UsagePage = 0xFF01
+pp_data->cap[122]->ReportID = 0x80
+pp_data->cap[122]->BitPosition = 0
+pp_data->cap[122]->BitSize = 8
+pp_data->cap[122]->ReportCount = 1
+pp_data->cap[122]->BytePosition = 0x003B
+pp_data->cap[122]->BitCount = 8
+pp_data->cap[122]->BitField = 0x02
+pp_data->cap[122]->NextBytePosition = 0x003C
+pp_data->cap[122]->LinkCollection = 0x0003
+pp_data->cap[122]->LinkUsagePage = 0xFF01
+pp_data->cap[122]->LinkUsage = 0x0080
+pp_data->cap[122]->IsMultipleItemsForArray = 0
+pp_data->cap[122]->IsButtonCap = 0
+pp_data->cap[122]->IsPadding = 0
+pp_data->cap[122]->IsAbsolute = 1
+pp_data->cap[122]->IsRange = 0
+pp_data->cap[122]->IsAlias = 0
+pp_data->cap[122]->IsStringRange = 0
+pp_data->cap[122]->IsDesignatorRange = 0
+pp_data->cap[122]->Reserved1 = 0x000000
+pp_data->cap[122]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[122]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[122]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[122]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[122]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[122]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[122]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[122]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[122]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[122]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[122]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[122]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[122]->NotRange.Usage = 0x0081
+pp_data->cap[122]->NotRange.Reserved1 = 0x0081
+pp_data->cap[122]->NotRange.StringIndex = 0
+pp_data->cap[122]->NotRange.Reserved2 = 0
+pp_data->cap[122]->NotRange.DesignatorIndex = 0
+pp_data->cap[122]->NotRange.Reserved3 = 0
+pp_data->cap[122]->NotRange.DataIndex = 35
+pp_data->cap[122]->NotRange.Reserved4 = 35
+pp_data->cap[122]->NotButton.HasNull = 0
+pp_data->cap[122]->NotButton.Reserved4 = 0x000000
+pp_data->cap[122]->NotButton.LogicalMin = 0
+pp_data->cap[122]->NotButton.LogicalMax = 127
+pp_data->cap[122]->NotButton.PhysicalMin = 0
+pp_data->cap[122]->NotButton.PhysicalMax = 0
+pp_data->cap[122]->Units = 0
+pp_data->cap[122]->UnitsExp = 0
+
+pp_data->cap[123]->UsagePage = 0xFF01
+pp_data->cap[123]->ReportID = 0x80
+pp_data->cap[123]->BitPosition = 0
+pp_data->cap[123]->BitSize = 8
+pp_data->cap[123]->ReportCount = 1
+pp_data->cap[123]->BytePosition = 0x003A
+pp_data->cap[123]->BitCount = 8
+pp_data->cap[123]->BitField = 0x02
+pp_data->cap[123]->NextBytePosition = 0x003B
+pp_data->cap[123]->LinkCollection = 0x0003
+pp_data->cap[123]->LinkUsagePage = 0xFF01
+pp_data->cap[123]->LinkUsage = 0x0080
+pp_data->cap[123]->IsMultipleItemsForArray = 0
+pp_data->cap[123]->IsButtonCap = 0
+pp_data->cap[123]->IsPadding = 0
+pp_data->cap[123]->IsAbsolute = 1
+pp_data->cap[123]->IsRange = 0
+pp_data->cap[123]->IsAlias = 0
+pp_data->cap[123]->IsStringRange = 0
+pp_data->cap[123]->IsDesignatorRange = 0
+pp_data->cap[123]->Reserved1 = 0x000000
+pp_data->cap[123]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[123]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[123]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[123]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[123]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[123]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[123]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[123]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[123]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[123]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[123]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[123]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[123]->NotRange.Usage = 0x0081
+pp_data->cap[123]->NotRange.Reserved1 = 0x0081
+pp_data->cap[123]->NotRange.StringIndex = 0
+pp_data->cap[123]->NotRange.Reserved2 = 0
+pp_data->cap[123]->NotRange.DesignatorIndex = 0
+pp_data->cap[123]->NotRange.Reserved3 = 0
+pp_data->cap[123]->NotRange.DataIndex = 36
+pp_data->cap[123]->NotRange.Reserved4 = 36
+pp_data->cap[123]->NotButton.HasNull = 0
+pp_data->cap[123]->NotButton.Reserved4 = 0x000000
+pp_data->cap[123]->NotButton.LogicalMin = 0
+pp_data->cap[123]->NotButton.LogicalMax = 127
+pp_data->cap[123]->NotButton.PhysicalMin = 0
+pp_data->cap[123]->NotButton.PhysicalMax = 0
+pp_data->cap[123]->Units = 0
+pp_data->cap[123]->UnitsExp = 0
+
+pp_data->cap[124]->UsagePage = 0xFF01
+pp_data->cap[124]->ReportID = 0x80
+pp_data->cap[124]->BitPosition = 0
+pp_data->cap[124]->BitSize = 8
+pp_data->cap[124]->ReportCount = 1
+pp_data->cap[124]->BytePosition = 0x0039
+pp_data->cap[124]->BitCount = 8
+pp_data->cap[124]->BitField = 0x02
+pp_data->cap[124]->NextBytePosition = 0x003A
+pp_data->cap[124]->LinkCollection = 0x0003
+pp_data->cap[124]->LinkUsagePage = 0xFF01
+pp_data->cap[124]->LinkUsage = 0x0080
+pp_data->cap[124]->IsMultipleItemsForArray = 0
+pp_data->cap[124]->IsButtonCap = 0
+pp_data->cap[124]->IsPadding = 0
+pp_data->cap[124]->IsAbsolute = 1
+pp_data->cap[124]->IsRange = 0
+pp_data->cap[124]->IsAlias = 0
+pp_data->cap[124]->IsStringRange = 0
+pp_data->cap[124]->IsDesignatorRange = 0
+pp_data->cap[124]->Reserved1 = 0x000000
+pp_data->cap[124]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[124]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[124]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[124]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[124]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[124]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[124]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[124]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[124]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[124]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[124]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[124]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[124]->NotRange.Usage = 0x0081
+pp_data->cap[124]->NotRange.Reserved1 = 0x0081
+pp_data->cap[124]->NotRange.StringIndex = 0
+pp_data->cap[124]->NotRange.Reserved2 = 0
+pp_data->cap[124]->NotRange.DesignatorIndex = 0
+pp_data->cap[124]->NotRange.Reserved3 = 0
+pp_data->cap[124]->NotRange.DataIndex = 37
+pp_data->cap[124]->NotRange.Reserved4 = 37
+pp_data->cap[124]->NotButton.HasNull = 0
+pp_data->cap[124]->NotButton.Reserved4 = 0x000000
+pp_data->cap[124]->NotButton.LogicalMin = 0
+pp_data->cap[124]->NotButton.LogicalMax = 127
+pp_data->cap[124]->NotButton.PhysicalMin = 0
+pp_data->cap[124]->NotButton.PhysicalMax = 0
+pp_data->cap[124]->Units = 0
+pp_data->cap[124]->UnitsExp = 0
+
+pp_data->cap[125]->UsagePage = 0xFF01
+pp_data->cap[125]->ReportID = 0x80
+pp_data->cap[125]->BitPosition = 0
+pp_data->cap[125]->BitSize = 8
+pp_data->cap[125]->ReportCount = 1
+pp_data->cap[125]->BytePosition = 0x0038
+pp_data->cap[125]->BitCount = 8
+pp_data->cap[125]->BitField = 0x02
+pp_data->cap[125]->NextBytePosition = 0x0039
+pp_data->cap[125]->LinkCollection = 0x0003
+pp_data->cap[125]->LinkUsagePage = 0xFF01
+pp_data->cap[125]->LinkUsage = 0x0080
+pp_data->cap[125]->IsMultipleItemsForArray = 0
+pp_data->cap[125]->IsButtonCap = 0
+pp_data->cap[125]->IsPadding = 0
+pp_data->cap[125]->IsAbsolute = 1
+pp_data->cap[125]->IsRange = 0
+pp_data->cap[125]->IsAlias = 0
+pp_data->cap[125]->IsStringRange = 0
+pp_data->cap[125]->IsDesignatorRange = 0
+pp_data->cap[125]->Reserved1 = 0x000000
+pp_data->cap[125]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[125]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[125]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[125]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[125]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[125]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[125]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[125]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[125]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[125]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[125]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[125]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[125]->NotRange.Usage = 0x0081
+pp_data->cap[125]->NotRange.Reserved1 = 0x0081
+pp_data->cap[125]->NotRange.StringIndex = 0
+pp_data->cap[125]->NotRange.Reserved2 = 0
+pp_data->cap[125]->NotRange.DesignatorIndex = 0
+pp_data->cap[125]->NotRange.Reserved3 = 0
+pp_data->cap[125]->NotRange.DataIndex = 38
+pp_data->cap[125]->NotRange.Reserved4 = 38
+pp_data->cap[125]->NotButton.HasNull = 0
+pp_data->cap[125]->NotButton.Reserved4 = 0x000000
+pp_data->cap[125]->NotButton.LogicalMin = 0
+pp_data->cap[125]->NotButton.LogicalMax = 127
+pp_data->cap[125]->NotButton.PhysicalMin = 0
+pp_data->cap[125]->NotButton.PhysicalMax = 0
+pp_data->cap[125]->Units = 0
+pp_data->cap[125]->UnitsExp = 0
+
+pp_data->cap[126]->UsagePage = 0xFF01
+pp_data->cap[126]->ReportID = 0x80
+pp_data->cap[126]->BitPosition = 0
+pp_data->cap[126]->BitSize = 8
+pp_data->cap[126]->ReportCount = 1
+pp_data->cap[126]->BytePosition = 0x0037
+pp_data->cap[126]->BitCount = 8
+pp_data->cap[126]->BitField = 0x02
+pp_data->cap[126]->NextBytePosition = 0x0038
+pp_data->cap[126]->LinkCollection = 0x0003
+pp_data->cap[126]->LinkUsagePage = 0xFF01
+pp_data->cap[126]->LinkUsage = 0x0080
+pp_data->cap[126]->IsMultipleItemsForArray = 0
+pp_data->cap[126]->IsButtonCap = 0
+pp_data->cap[126]->IsPadding = 0
+pp_data->cap[126]->IsAbsolute = 1
+pp_data->cap[126]->IsRange = 0
+pp_data->cap[126]->IsAlias = 0
+pp_data->cap[126]->IsStringRange = 0
+pp_data->cap[126]->IsDesignatorRange = 0
+pp_data->cap[126]->Reserved1 = 0x000000
+pp_data->cap[126]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[126]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[126]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[126]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[126]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[126]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[126]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[126]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[126]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[126]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[126]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[126]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[126]->NotRange.Usage = 0x0081
+pp_data->cap[126]->NotRange.Reserved1 = 0x0081
+pp_data->cap[126]->NotRange.StringIndex = 0
+pp_data->cap[126]->NotRange.Reserved2 = 0
+pp_data->cap[126]->NotRange.DesignatorIndex = 0
+pp_data->cap[126]->NotRange.Reserved3 = 0
+pp_data->cap[126]->NotRange.DataIndex = 39
+pp_data->cap[126]->NotRange.Reserved4 = 39
+pp_data->cap[126]->NotButton.HasNull = 0
+pp_data->cap[126]->NotButton.Reserved4 = 0x000000
+pp_data->cap[126]->NotButton.LogicalMin = 0
+pp_data->cap[126]->NotButton.LogicalMax = 127
+pp_data->cap[126]->NotButton.PhysicalMin = 0
+pp_data->cap[126]->NotButton.PhysicalMax = 0
+pp_data->cap[126]->Units = 0
+pp_data->cap[126]->UnitsExp = 0
+
+pp_data->cap[127]->UsagePage = 0xFF01
+pp_data->cap[127]->ReportID = 0x80
+pp_data->cap[127]->BitPosition = 0
+pp_data->cap[127]->BitSize = 8
+pp_data->cap[127]->ReportCount = 1
+pp_data->cap[127]->BytePosition = 0x0036
+pp_data->cap[127]->BitCount = 8
+pp_data->cap[127]->BitField = 0x02
+pp_data->cap[127]->NextBytePosition = 0x0037
+pp_data->cap[127]->LinkCollection = 0x0003
+pp_data->cap[127]->LinkUsagePage = 0xFF01
+pp_data->cap[127]->LinkUsage = 0x0080
+pp_data->cap[127]->IsMultipleItemsForArray = 0
+pp_data->cap[127]->IsButtonCap = 0
+pp_data->cap[127]->IsPadding = 0
+pp_data->cap[127]->IsAbsolute = 1
+pp_data->cap[127]->IsRange = 0
+pp_data->cap[127]->IsAlias = 0
+pp_data->cap[127]->IsStringRange = 0
+pp_data->cap[127]->IsDesignatorRange = 0
+pp_data->cap[127]->Reserved1 = 0x000000
+pp_data->cap[127]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[127]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[127]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[127]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[127]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[127]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[127]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[127]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[127]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[127]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[127]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[127]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[127]->NotRange.Usage = 0x0081
+pp_data->cap[127]->NotRange.Reserved1 = 0x0081
+pp_data->cap[127]->NotRange.StringIndex = 0
+pp_data->cap[127]->NotRange.Reserved2 = 0
+pp_data->cap[127]->NotRange.DesignatorIndex = 0
+pp_data->cap[127]->NotRange.Reserved3 = 0
+pp_data->cap[127]->NotRange.DataIndex = 40
+pp_data->cap[127]->NotRange.Reserved4 = 40
+pp_data->cap[127]->NotButton.HasNull = 0
+pp_data->cap[127]->NotButton.Reserved4 = 0x000000
+pp_data->cap[127]->NotButton.LogicalMin = 0
+pp_data->cap[127]->NotButton.LogicalMax = 127
+pp_data->cap[127]->NotButton.PhysicalMin = 0
+pp_data->cap[127]->NotButton.PhysicalMax = 0
+pp_data->cap[127]->Units = 0
+pp_data->cap[127]->UnitsExp = 0
+
+pp_data->cap[128]->UsagePage = 0xFF01
+pp_data->cap[128]->ReportID = 0x80
+pp_data->cap[128]->BitPosition = 0
+pp_data->cap[128]->BitSize = 8
+pp_data->cap[128]->ReportCount = 1
+pp_data->cap[128]->BytePosition = 0x0035
+pp_data->cap[128]->BitCount = 8
+pp_data->cap[128]->BitField = 0x02
+pp_data->cap[128]->NextBytePosition = 0x0036
+pp_data->cap[128]->LinkCollection = 0x0003
+pp_data->cap[128]->LinkUsagePage = 0xFF01
+pp_data->cap[128]->LinkUsage = 0x0080
+pp_data->cap[128]->IsMultipleItemsForArray = 0
+pp_data->cap[128]->IsButtonCap = 0
+pp_data->cap[128]->IsPadding = 0
+pp_data->cap[128]->IsAbsolute = 1
+pp_data->cap[128]->IsRange = 0
+pp_data->cap[128]->IsAlias = 0
+pp_data->cap[128]->IsStringRange = 0
+pp_data->cap[128]->IsDesignatorRange = 0
+pp_data->cap[128]->Reserved1 = 0x000000
+pp_data->cap[128]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[128]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[128]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[128]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[128]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[128]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[128]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[128]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[128]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[128]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[128]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[128]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[128]->NotRange.Usage = 0x0081
+pp_data->cap[128]->NotRange.Reserved1 = 0x0081
+pp_data->cap[128]->NotRange.StringIndex = 0
+pp_data->cap[128]->NotRange.Reserved2 = 0
+pp_data->cap[128]->NotRange.DesignatorIndex = 0
+pp_data->cap[128]->NotRange.Reserved3 = 0
+pp_data->cap[128]->NotRange.DataIndex = 41
+pp_data->cap[128]->NotRange.Reserved4 = 41
+pp_data->cap[128]->NotButton.HasNull = 0
+pp_data->cap[128]->NotButton.Reserved4 = 0x000000
+pp_data->cap[128]->NotButton.LogicalMin = 0
+pp_data->cap[128]->NotButton.LogicalMax = 127
+pp_data->cap[128]->NotButton.PhysicalMin = 0
+pp_data->cap[128]->NotButton.PhysicalMax = 0
+pp_data->cap[128]->Units = 0
+pp_data->cap[128]->UnitsExp = 0
+
+pp_data->cap[129]->UsagePage = 0xFF01
+pp_data->cap[129]->ReportID = 0x80
+pp_data->cap[129]->BitPosition = 0
+pp_data->cap[129]->BitSize = 8
+pp_data->cap[129]->ReportCount = 1
+pp_data->cap[129]->BytePosition = 0x0034
+pp_data->cap[129]->BitCount = 8
+pp_data->cap[129]->BitField = 0x02
+pp_data->cap[129]->NextBytePosition = 0x0035
+pp_data->cap[129]->LinkCollection = 0x0003
+pp_data->cap[129]->LinkUsagePage = 0xFF01
+pp_data->cap[129]->LinkUsage = 0x0080
+pp_data->cap[129]->IsMultipleItemsForArray = 0
+pp_data->cap[129]->IsButtonCap = 0
+pp_data->cap[129]->IsPadding = 0
+pp_data->cap[129]->IsAbsolute = 1
+pp_data->cap[129]->IsRange = 0
+pp_data->cap[129]->IsAlias = 0
+pp_data->cap[129]->IsStringRange = 0
+pp_data->cap[129]->IsDesignatorRange = 0
+pp_data->cap[129]->Reserved1 = 0x000000
+pp_data->cap[129]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[129]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[129]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[129]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[129]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[129]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[129]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[129]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[129]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[129]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[129]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[129]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[129]->NotRange.Usage = 0x0081
+pp_data->cap[129]->NotRange.Reserved1 = 0x0081
+pp_data->cap[129]->NotRange.StringIndex = 0
+pp_data->cap[129]->NotRange.Reserved2 = 0
+pp_data->cap[129]->NotRange.DesignatorIndex = 0
+pp_data->cap[129]->NotRange.Reserved3 = 0
+pp_data->cap[129]->NotRange.DataIndex = 42
+pp_data->cap[129]->NotRange.Reserved4 = 42
+pp_data->cap[129]->NotButton.HasNull = 0
+pp_data->cap[129]->NotButton.Reserved4 = 0x000000
+pp_data->cap[129]->NotButton.LogicalMin = 0
+pp_data->cap[129]->NotButton.LogicalMax = 127
+pp_data->cap[129]->NotButton.PhysicalMin = 0
+pp_data->cap[129]->NotButton.PhysicalMax = 0
+pp_data->cap[129]->Units = 0
+pp_data->cap[129]->UnitsExp = 0
+
+pp_data->cap[130]->UsagePage = 0xFF01
+pp_data->cap[130]->ReportID = 0x80
+pp_data->cap[130]->BitPosition = 0
+pp_data->cap[130]->BitSize = 8
+pp_data->cap[130]->ReportCount = 1
+pp_data->cap[130]->BytePosition = 0x0033
+pp_data->cap[130]->BitCount = 8
+pp_data->cap[130]->BitField = 0x02
+pp_data->cap[130]->NextBytePosition = 0x0034
+pp_data->cap[130]->LinkCollection = 0x0003
+pp_data->cap[130]->LinkUsagePage = 0xFF01
+pp_data->cap[130]->LinkUsage = 0x0080
+pp_data->cap[130]->IsMultipleItemsForArray = 0
+pp_data->cap[130]->IsButtonCap = 0
+pp_data->cap[130]->IsPadding = 0
+pp_data->cap[130]->IsAbsolute = 1
+pp_data->cap[130]->IsRange = 0
+pp_data->cap[130]->IsAlias = 0
+pp_data->cap[130]->IsStringRange = 0
+pp_data->cap[130]->IsDesignatorRange = 0
+pp_data->cap[130]->Reserved1 = 0x000000
+pp_data->cap[130]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[130]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[130]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[130]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[130]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[130]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[130]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[130]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[130]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[130]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[130]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[130]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[130]->NotRange.Usage = 0x0081
+pp_data->cap[130]->NotRange.Reserved1 = 0x0081
+pp_data->cap[130]->NotRange.StringIndex = 0
+pp_data->cap[130]->NotRange.Reserved2 = 0
+pp_data->cap[130]->NotRange.DesignatorIndex = 0
+pp_data->cap[130]->NotRange.Reserved3 = 0
+pp_data->cap[130]->NotRange.DataIndex = 43
+pp_data->cap[130]->NotRange.Reserved4 = 43
+pp_data->cap[130]->NotButton.HasNull = 0
+pp_data->cap[130]->NotButton.Reserved4 = 0x000000
+pp_data->cap[130]->NotButton.LogicalMin = 0
+pp_data->cap[130]->NotButton.LogicalMax = 127
+pp_data->cap[130]->NotButton.PhysicalMin = 0
+pp_data->cap[130]->NotButton.PhysicalMax = 0
+pp_data->cap[130]->Units = 0
+pp_data->cap[130]->UnitsExp = 0
+
+pp_data->cap[131]->UsagePage = 0xFF01
+pp_data->cap[131]->ReportID = 0x80
+pp_data->cap[131]->BitPosition = 0
+pp_data->cap[131]->BitSize = 8
+pp_data->cap[131]->ReportCount = 1
+pp_data->cap[131]->BytePosition = 0x0032
+pp_data->cap[131]->BitCount = 8
+pp_data->cap[131]->BitField = 0x02
+pp_data->cap[131]->NextBytePosition = 0x0033
+pp_data->cap[131]->LinkCollection = 0x0003
+pp_data->cap[131]->LinkUsagePage = 0xFF01
+pp_data->cap[131]->LinkUsage = 0x0080
+pp_data->cap[131]->IsMultipleItemsForArray = 0
+pp_data->cap[131]->IsButtonCap = 0
+pp_data->cap[131]->IsPadding = 0
+pp_data->cap[131]->IsAbsolute = 1
+pp_data->cap[131]->IsRange = 0
+pp_data->cap[131]->IsAlias = 0
+pp_data->cap[131]->IsStringRange = 0
+pp_data->cap[131]->IsDesignatorRange = 0
+pp_data->cap[131]->Reserved1 = 0x000000
+pp_data->cap[131]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[131]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[131]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[131]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[131]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[131]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[131]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[131]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[131]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[131]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[131]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[131]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[131]->NotRange.Usage = 0x0081
+pp_data->cap[131]->NotRange.Reserved1 = 0x0081
+pp_data->cap[131]->NotRange.StringIndex = 0
+pp_data->cap[131]->NotRange.Reserved2 = 0
+pp_data->cap[131]->NotRange.DesignatorIndex = 0
+pp_data->cap[131]->NotRange.Reserved3 = 0
+pp_data->cap[131]->NotRange.DataIndex = 44
+pp_data->cap[131]->NotRange.Reserved4 = 44
+pp_data->cap[131]->NotButton.HasNull = 0
+pp_data->cap[131]->NotButton.Reserved4 = 0x000000
+pp_data->cap[131]->NotButton.LogicalMin = 0
+pp_data->cap[131]->NotButton.LogicalMax = 127
+pp_data->cap[131]->NotButton.PhysicalMin = 0
+pp_data->cap[131]->NotButton.PhysicalMax = 0
+pp_data->cap[131]->Units = 0
+pp_data->cap[131]->UnitsExp = 0
+
+pp_data->cap[132]->UsagePage = 0xFF01
+pp_data->cap[132]->ReportID = 0x80
+pp_data->cap[132]->BitPosition = 0
+pp_data->cap[132]->BitSize = 8
+pp_data->cap[132]->ReportCount = 1
+pp_data->cap[132]->BytePosition = 0x0031
+pp_data->cap[132]->BitCount = 8
+pp_data->cap[132]->BitField = 0x02
+pp_data->cap[132]->NextBytePosition = 0x0032
+pp_data->cap[132]->LinkCollection = 0x0003
+pp_data->cap[132]->LinkUsagePage = 0xFF01
+pp_data->cap[132]->LinkUsage = 0x0080
+pp_data->cap[132]->IsMultipleItemsForArray = 0
+pp_data->cap[132]->IsButtonCap = 0
+pp_data->cap[132]->IsPadding = 0
+pp_data->cap[132]->IsAbsolute = 1
+pp_data->cap[132]->IsRange = 0
+pp_data->cap[132]->IsAlias = 0
+pp_data->cap[132]->IsStringRange = 0
+pp_data->cap[132]->IsDesignatorRange = 0
+pp_data->cap[132]->Reserved1 = 0x000000
+pp_data->cap[132]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[132]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[132]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[132]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[132]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[132]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[132]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[132]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[132]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[132]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[132]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[132]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[132]->NotRange.Usage = 0x0081
+pp_data->cap[132]->NotRange.Reserved1 = 0x0081
+pp_data->cap[132]->NotRange.StringIndex = 0
+pp_data->cap[132]->NotRange.Reserved2 = 0
+pp_data->cap[132]->NotRange.DesignatorIndex = 0
+pp_data->cap[132]->NotRange.Reserved3 = 0
+pp_data->cap[132]->NotRange.DataIndex = 45
+pp_data->cap[132]->NotRange.Reserved4 = 45
+pp_data->cap[132]->NotButton.HasNull = 0
+pp_data->cap[132]->NotButton.Reserved4 = 0x000000
+pp_data->cap[132]->NotButton.LogicalMin = 0
+pp_data->cap[132]->NotButton.LogicalMax = 127
+pp_data->cap[132]->NotButton.PhysicalMin = 0
+pp_data->cap[132]->NotButton.PhysicalMax = 0
+pp_data->cap[132]->Units = 0
+pp_data->cap[132]->UnitsExp = 0
+
+pp_data->cap[133]->UsagePage = 0xFF01
+pp_data->cap[133]->ReportID = 0x80
+pp_data->cap[133]->BitPosition = 0
+pp_data->cap[133]->BitSize = 8
+pp_data->cap[133]->ReportCount = 1
+pp_data->cap[133]->BytePosition = 0x0030
+pp_data->cap[133]->BitCount = 8
+pp_data->cap[133]->BitField = 0x02
+pp_data->cap[133]->NextBytePosition = 0x0031
+pp_data->cap[133]->LinkCollection = 0x0003
+pp_data->cap[133]->LinkUsagePage = 0xFF01
+pp_data->cap[133]->LinkUsage = 0x0080
+pp_data->cap[133]->IsMultipleItemsForArray = 0
+pp_data->cap[133]->IsButtonCap = 0
+pp_data->cap[133]->IsPadding = 0
+pp_data->cap[133]->IsAbsolute = 1
+pp_data->cap[133]->IsRange = 0
+pp_data->cap[133]->IsAlias = 0
+pp_data->cap[133]->IsStringRange = 0
+pp_data->cap[133]->IsDesignatorRange = 0
+pp_data->cap[133]->Reserved1 = 0x000000
+pp_data->cap[133]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[133]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[133]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[133]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[133]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[133]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[133]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[133]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[133]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[133]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[133]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[133]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[133]->NotRange.Usage = 0x0081
+pp_data->cap[133]->NotRange.Reserved1 = 0x0081
+pp_data->cap[133]->NotRange.StringIndex = 0
+pp_data->cap[133]->NotRange.Reserved2 = 0
+pp_data->cap[133]->NotRange.DesignatorIndex = 0
+pp_data->cap[133]->NotRange.Reserved3 = 0
+pp_data->cap[133]->NotRange.DataIndex = 46
+pp_data->cap[133]->NotRange.Reserved4 = 46
+pp_data->cap[133]->NotButton.HasNull = 0
+pp_data->cap[133]->NotButton.Reserved4 = 0x000000
+pp_data->cap[133]->NotButton.LogicalMin = 0
+pp_data->cap[133]->NotButton.LogicalMax = 127
+pp_data->cap[133]->NotButton.PhysicalMin = 0
+pp_data->cap[133]->NotButton.PhysicalMax = 0
+pp_data->cap[133]->Units = 0
+pp_data->cap[133]->UnitsExp = 0
+
+pp_data->cap[134]->UsagePage = 0xFF01
+pp_data->cap[134]->ReportID = 0x80
+pp_data->cap[134]->BitPosition = 0
+pp_data->cap[134]->BitSize = 8
+pp_data->cap[134]->ReportCount = 1
+pp_data->cap[134]->BytePosition = 0x002F
+pp_data->cap[134]->BitCount = 8
+pp_data->cap[134]->BitField = 0x02
+pp_data->cap[134]->NextBytePosition = 0x0030
+pp_data->cap[134]->LinkCollection = 0x0003
+pp_data->cap[134]->LinkUsagePage = 0xFF01
+pp_data->cap[134]->LinkUsage = 0x0080
+pp_data->cap[134]->IsMultipleItemsForArray = 0
+pp_data->cap[134]->IsButtonCap = 0
+pp_data->cap[134]->IsPadding = 0
+pp_data->cap[134]->IsAbsolute = 1
+pp_data->cap[134]->IsRange = 0
+pp_data->cap[134]->IsAlias = 0
+pp_data->cap[134]->IsStringRange = 0
+pp_data->cap[134]->IsDesignatorRange = 0
+pp_data->cap[134]->Reserved1 = 0x000000
+pp_data->cap[134]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[134]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[134]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[134]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[134]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[134]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[134]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[134]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[134]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[134]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[134]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[134]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[134]->NotRange.Usage = 0x0081
+pp_data->cap[134]->NotRange.Reserved1 = 0x0081
+pp_data->cap[134]->NotRange.StringIndex = 0
+pp_data->cap[134]->NotRange.Reserved2 = 0
+pp_data->cap[134]->NotRange.DesignatorIndex = 0
+pp_data->cap[134]->NotRange.Reserved3 = 0
+pp_data->cap[134]->NotRange.DataIndex = 47
+pp_data->cap[134]->NotRange.Reserved4 = 47
+pp_data->cap[134]->NotButton.HasNull = 0
+pp_data->cap[134]->NotButton.Reserved4 = 0x000000
+pp_data->cap[134]->NotButton.LogicalMin = 0
+pp_data->cap[134]->NotButton.LogicalMax = 127
+pp_data->cap[134]->NotButton.PhysicalMin = 0
+pp_data->cap[134]->NotButton.PhysicalMax = 0
+pp_data->cap[134]->Units = 0
+pp_data->cap[134]->UnitsExp = 0
+
+pp_data->cap[135]->UsagePage = 0xFF01
+pp_data->cap[135]->ReportID = 0x80
+pp_data->cap[135]->BitPosition = 0
+pp_data->cap[135]->BitSize = 8
+pp_data->cap[135]->ReportCount = 1
+pp_data->cap[135]->BytePosition = 0x002E
+pp_data->cap[135]->BitCount = 8
+pp_data->cap[135]->BitField = 0x02
+pp_data->cap[135]->NextBytePosition = 0x002F
+pp_data->cap[135]->LinkCollection = 0x0003
+pp_data->cap[135]->LinkUsagePage = 0xFF01
+pp_data->cap[135]->LinkUsage = 0x0080
+pp_data->cap[135]->IsMultipleItemsForArray = 0
+pp_data->cap[135]->IsButtonCap = 0
+pp_data->cap[135]->IsPadding = 0
+pp_data->cap[135]->IsAbsolute = 1
+pp_data->cap[135]->IsRange = 0
+pp_data->cap[135]->IsAlias = 0
+pp_data->cap[135]->IsStringRange = 0
+pp_data->cap[135]->IsDesignatorRange = 0
+pp_data->cap[135]->Reserved1 = 0x000000
+pp_data->cap[135]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[135]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[135]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[135]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[135]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[135]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[135]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[135]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[135]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[135]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[135]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[135]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[135]->NotRange.Usage = 0x0081
+pp_data->cap[135]->NotRange.Reserved1 = 0x0081
+pp_data->cap[135]->NotRange.StringIndex = 0
+pp_data->cap[135]->NotRange.Reserved2 = 0
+pp_data->cap[135]->NotRange.DesignatorIndex = 0
+pp_data->cap[135]->NotRange.Reserved3 = 0
+pp_data->cap[135]->NotRange.DataIndex = 48
+pp_data->cap[135]->NotRange.Reserved4 = 48
+pp_data->cap[135]->NotButton.HasNull = 0
+pp_data->cap[135]->NotButton.Reserved4 = 0x000000
+pp_data->cap[135]->NotButton.LogicalMin = 0
+pp_data->cap[135]->NotButton.LogicalMax = 127
+pp_data->cap[135]->NotButton.PhysicalMin = 0
+pp_data->cap[135]->NotButton.PhysicalMax = 0
+pp_data->cap[135]->Units = 0
+pp_data->cap[135]->UnitsExp = 0
+
+pp_data->cap[136]->UsagePage = 0xFF01
+pp_data->cap[136]->ReportID = 0x80
+pp_data->cap[136]->BitPosition = 0
+pp_data->cap[136]->BitSize = 8
+pp_data->cap[136]->ReportCount = 1
+pp_data->cap[136]->BytePosition = 0x002D
+pp_data->cap[136]->BitCount = 8
+pp_data->cap[136]->BitField = 0x02
+pp_data->cap[136]->NextBytePosition = 0x002E
+pp_data->cap[136]->LinkCollection = 0x0003
+pp_data->cap[136]->LinkUsagePage = 0xFF01
+pp_data->cap[136]->LinkUsage = 0x0080
+pp_data->cap[136]->IsMultipleItemsForArray = 0
+pp_data->cap[136]->IsButtonCap = 0
+pp_data->cap[136]->IsPadding = 0
+pp_data->cap[136]->IsAbsolute = 1
+pp_data->cap[136]->IsRange = 0
+pp_data->cap[136]->IsAlias = 0
+pp_data->cap[136]->IsStringRange = 0
+pp_data->cap[136]->IsDesignatorRange = 0
+pp_data->cap[136]->Reserved1 = 0x000000
+pp_data->cap[136]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[136]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[136]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[136]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[136]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[136]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[136]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[136]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[136]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[136]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[136]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[136]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[136]->NotRange.Usage = 0x0081
+pp_data->cap[136]->NotRange.Reserved1 = 0x0081
+pp_data->cap[136]->NotRange.StringIndex = 0
+pp_data->cap[136]->NotRange.Reserved2 = 0
+pp_data->cap[136]->NotRange.DesignatorIndex = 0
+pp_data->cap[136]->NotRange.Reserved3 = 0
+pp_data->cap[136]->NotRange.DataIndex = 49
+pp_data->cap[136]->NotRange.Reserved4 = 49
+pp_data->cap[136]->NotButton.HasNull = 0
+pp_data->cap[136]->NotButton.Reserved4 = 0x000000
+pp_data->cap[136]->NotButton.LogicalMin = 0
+pp_data->cap[136]->NotButton.LogicalMax = 127
+pp_data->cap[136]->NotButton.PhysicalMin = 0
+pp_data->cap[136]->NotButton.PhysicalMax = 0
+pp_data->cap[136]->Units = 0
+pp_data->cap[136]->UnitsExp = 0
+
+pp_data->cap[137]->UsagePage = 0xFF01
+pp_data->cap[137]->ReportID = 0x80
+pp_data->cap[137]->BitPosition = 0
+pp_data->cap[137]->BitSize = 8
+pp_data->cap[137]->ReportCount = 1
+pp_data->cap[137]->BytePosition = 0x002C
+pp_data->cap[137]->BitCount = 8
+pp_data->cap[137]->BitField = 0x02
+pp_data->cap[137]->NextBytePosition = 0x002D
+pp_data->cap[137]->LinkCollection = 0x0003
+pp_data->cap[137]->LinkUsagePage = 0xFF01
+pp_data->cap[137]->LinkUsage = 0x0080
+pp_data->cap[137]->IsMultipleItemsForArray = 0
+pp_data->cap[137]->IsButtonCap = 0
+pp_data->cap[137]->IsPadding = 0
+pp_data->cap[137]->IsAbsolute = 1
+pp_data->cap[137]->IsRange = 0
+pp_data->cap[137]->IsAlias = 0
+pp_data->cap[137]->IsStringRange = 0
+pp_data->cap[137]->IsDesignatorRange = 0
+pp_data->cap[137]->Reserved1 = 0x000000
+pp_data->cap[137]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[137]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[137]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[137]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[137]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[137]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[137]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[137]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[137]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[137]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[137]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[137]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[137]->NotRange.Usage = 0x0081
+pp_data->cap[137]->NotRange.Reserved1 = 0x0081
+pp_data->cap[137]->NotRange.StringIndex = 0
+pp_data->cap[137]->NotRange.Reserved2 = 0
+pp_data->cap[137]->NotRange.DesignatorIndex = 0
+pp_data->cap[137]->NotRange.Reserved3 = 0
+pp_data->cap[137]->NotRange.DataIndex = 50
+pp_data->cap[137]->NotRange.Reserved4 = 50
+pp_data->cap[137]->NotButton.HasNull = 0
+pp_data->cap[137]->NotButton.Reserved4 = 0x000000
+pp_data->cap[137]->NotButton.LogicalMin = 0
+pp_data->cap[137]->NotButton.LogicalMax = 127
+pp_data->cap[137]->NotButton.PhysicalMin = 0
+pp_data->cap[137]->NotButton.PhysicalMax = 0
+pp_data->cap[137]->Units = 0
+pp_data->cap[137]->UnitsExp = 0
+
+pp_data->cap[138]->UsagePage = 0xFF01
+pp_data->cap[138]->ReportID = 0x80
+pp_data->cap[138]->BitPosition = 0
+pp_data->cap[138]->BitSize = 8
+pp_data->cap[138]->ReportCount = 1
+pp_data->cap[138]->BytePosition = 0x002B
+pp_data->cap[138]->BitCount = 8
+pp_data->cap[138]->BitField = 0x02
+pp_data->cap[138]->NextBytePosition = 0x002C
+pp_data->cap[138]->LinkCollection = 0x0003
+pp_data->cap[138]->LinkUsagePage = 0xFF01
+pp_data->cap[138]->LinkUsage = 0x0080
+pp_data->cap[138]->IsMultipleItemsForArray = 0
+pp_data->cap[138]->IsButtonCap = 0
+pp_data->cap[138]->IsPadding = 0
+pp_data->cap[138]->IsAbsolute = 1
+pp_data->cap[138]->IsRange = 0
+pp_data->cap[138]->IsAlias = 0
+pp_data->cap[138]->IsStringRange = 0
+pp_data->cap[138]->IsDesignatorRange = 0
+pp_data->cap[138]->Reserved1 = 0x000000
+pp_data->cap[138]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[138]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[138]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[138]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[138]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[138]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[138]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[138]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[138]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[138]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[138]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[138]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[138]->NotRange.Usage = 0x0081
+pp_data->cap[138]->NotRange.Reserved1 = 0x0081
+pp_data->cap[138]->NotRange.StringIndex = 0
+pp_data->cap[138]->NotRange.Reserved2 = 0
+pp_data->cap[138]->NotRange.DesignatorIndex = 0
+pp_data->cap[138]->NotRange.Reserved3 = 0
+pp_data->cap[138]->NotRange.DataIndex = 51
+pp_data->cap[138]->NotRange.Reserved4 = 51
+pp_data->cap[138]->NotButton.HasNull = 0
+pp_data->cap[138]->NotButton.Reserved4 = 0x000000
+pp_data->cap[138]->NotButton.LogicalMin = 0
+pp_data->cap[138]->NotButton.LogicalMax = 127
+pp_data->cap[138]->NotButton.PhysicalMin = 0
+pp_data->cap[138]->NotButton.PhysicalMax = 0
+pp_data->cap[138]->Units = 0
+pp_data->cap[138]->UnitsExp = 0
+
+pp_data->cap[139]->UsagePage = 0xFF01
+pp_data->cap[139]->ReportID = 0x80
+pp_data->cap[139]->BitPosition = 0
+pp_data->cap[139]->BitSize = 8
+pp_data->cap[139]->ReportCount = 1
+pp_data->cap[139]->BytePosition = 0x002A
+pp_data->cap[139]->BitCount = 8
+pp_data->cap[139]->BitField = 0x02
+pp_data->cap[139]->NextBytePosition = 0x002B
+pp_data->cap[139]->LinkCollection = 0x0003
+pp_data->cap[139]->LinkUsagePage = 0xFF01
+pp_data->cap[139]->LinkUsage = 0x0080
+pp_data->cap[139]->IsMultipleItemsForArray = 0
+pp_data->cap[139]->IsButtonCap = 0
+pp_data->cap[139]->IsPadding = 0
+pp_data->cap[139]->IsAbsolute = 1
+pp_data->cap[139]->IsRange = 0
+pp_data->cap[139]->IsAlias = 0
+pp_data->cap[139]->IsStringRange = 0
+pp_data->cap[139]->IsDesignatorRange = 0
+pp_data->cap[139]->Reserved1 = 0x000000
+pp_data->cap[139]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[139]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[139]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[139]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[139]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[139]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[139]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[139]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[139]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[139]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[139]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[139]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[139]->NotRange.Usage = 0x0081
+pp_data->cap[139]->NotRange.Reserved1 = 0x0081
+pp_data->cap[139]->NotRange.StringIndex = 0
+pp_data->cap[139]->NotRange.Reserved2 = 0
+pp_data->cap[139]->NotRange.DesignatorIndex = 0
+pp_data->cap[139]->NotRange.Reserved3 = 0
+pp_data->cap[139]->NotRange.DataIndex = 52
+pp_data->cap[139]->NotRange.Reserved4 = 52
+pp_data->cap[139]->NotButton.HasNull = 0
+pp_data->cap[139]->NotButton.Reserved4 = 0x000000
+pp_data->cap[139]->NotButton.LogicalMin = 0
+pp_data->cap[139]->NotButton.LogicalMax = 127
+pp_data->cap[139]->NotButton.PhysicalMin = 0
+pp_data->cap[139]->NotButton.PhysicalMax = 0
+pp_data->cap[139]->Units = 0
+pp_data->cap[139]->UnitsExp = 0
+
+pp_data->cap[140]->UsagePage = 0xFF01
+pp_data->cap[140]->ReportID = 0x80
+pp_data->cap[140]->BitPosition = 0
+pp_data->cap[140]->BitSize = 8
+pp_data->cap[140]->ReportCount = 1
+pp_data->cap[140]->BytePosition = 0x0029
+pp_data->cap[140]->BitCount = 8
+pp_data->cap[140]->BitField = 0x02
+pp_data->cap[140]->NextBytePosition = 0x002A
+pp_data->cap[140]->LinkCollection = 0x0003
+pp_data->cap[140]->LinkUsagePage = 0xFF01
+pp_data->cap[140]->LinkUsage = 0x0080
+pp_data->cap[140]->IsMultipleItemsForArray = 0
+pp_data->cap[140]->IsButtonCap = 0
+pp_data->cap[140]->IsPadding = 0
+pp_data->cap[140]->IsAbsolute = 1
+pp_data->cap[140]->IsRange = 0
+pp_data->cap[140]->IsAlias = 0
+pp_data->cap[140]->IsStringRange = 0
+pp_data->cap[140]->IsDesignatorRange = 0
+pp_data->cap[140]->Reserved1 = 0x000000
+pp_data->cap[140]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[140]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[140]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[140]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[140]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[140]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[140]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[140]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[140]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[140]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[140]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[140]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[140]->NotRange.Usage = 0x0081
+pp_data->cap[140]->NotRange.Reserved1 = 0x0081
+pp_data->cap[140]->NotRange.StringIndex = 0
+pp_data->cap[140]->NotRange.Reserved2 = 0
+pp_data->cap[140]->NotRange.DesignatorIndex = 0
+pp_data->cap[140]->NotRange.Reserved3 = 0
+pp_data->cap[140]->NotRange.DataIndex = 53
+pp_data->cap[140]->NotRange.Reserved4 = 53
+pp_data->cap[140]->NotButton.HasNull = 0
+pp_data->cap[140]->NotButton.Reserved4 = 0x000000
+pp_data->cap[140]->NotButton.LogicalMin = 0
+pp_data->cap[140]->NotButton.LogicalMax = 127
+pp_data->cap[140]->NotButton.PhysicalMin = 0
+pp_data->cap[140]->NotButton.PhysicalMax = 0
+pp_data->cap[140]->Units = 0
+pp_data->cap[140]->UnitsExp = 0
+
+pp_data->cap[141]->UsagePage = 0xFF01
+pp_data->cap[141]->ReportID = 0x80
+pp_data->cap[141]->BitPosition = 0
+pp_data->cap[141]->BitSize = 8
+pp_data->cap[141]->ReportCount = 1
+pp_data->cap[141]->BytePosition = 0x0028
+pp_data->cap[141]->BitCount = 8
+pp_data->cap[141]->BitField = 0x02
+pp_data->cap[141]->NextBytePosition = 0x0029
+pp_data->cap[141]->LinkCollection = 0x0003
+pp_data->cap[141]->LinkUsagePage = 0xFF01
+pp_data->cap[141]->LinkUsage = 0x0080
+pp_data->cap[141]->IsMultipleItemsForArray = 0
+pp_data->cap[141]->IsButtonCap = 0
+pp_data->cap[141]->IsPadding = 0
+pp_data->cap[141]->IsAbsolute = 1
+pp_data->cap[141]->IsRange = 0
+pp_data->cap[141]->IsAlias = 0
+pp_data->cap[141]->IsStringRange = 0
+pp_data->cap[141]->IsDesignatorRange = 0
+pp_data->cap[141]->Reserved1 = 0x000000
+pp_data->cap[141]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[141]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[141]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[141]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[141]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[141]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[141]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[141]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[141]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[141]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[141]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[141]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[141]->NotRange.Usage = 0x0081
+pp_data->cap[141]->NotRange.Reserved1 = 0x0081
+pp_data->cap[141]->NotRange.StringIndex = 0
+pp_data->cap[141]->NotRange.Reserved2 = 0
+pp_data->cap[141]->NotRange.DesignatorIndex = 0
+pp_data->cap[141]->NotRange.Reserved3 = 0
+pp_data->cap[141]->NotRange.DataIndex = 54
+pp_data->cap[141]->NotRange.Reserved4 = 54
+pp_data->cap[141]->NotButton.HasNull = 0
+pp_data->cap[141]->NotButton.Reserved4 = 0x000000
+pp_data->cap[141]->NotButton.LogicalMin = 0
+pp_data->cap[141]->NotButton.LogicalMax = 127
+pp_data->cap[141]->NotButton.PhysicalMin = 0
+pp_data->cap[141]->NotButton.PhysicalMax = 0
+pp_data->cap[141]->Units = 0
+pp_data->cap[141]->UnitsExp = 0
+
+pp_data->cap[142]->UsagePage = 0xFF01
+pp_data->cap[142]->ReportID = 0x80
+pp_data->cap[142]->BitPosition = 0
+pp_data->cap[142]->BitSize = 8
+pp_data->cap[142]->ReportCount = 1
+pp_data->cap[142]->BytePosition = 0x0027
+pp_data->cap[142]->BitCount = 8
+pp_data->cap[142]->BitField = 0x02
+pp_data->cap[142]->NextBytePosition = 0x0028
+pp_data->cap[142]->LinkCollection = 0x0003
+pp_data->cap[142]->LinkUsagePage = 0xFF01
+pp_data->cap[142]->LinkUsage = 0x0080
+pp_data->cap[142]->IsMultipleItemsForArray = 0
+pp_data->cap[142]->IsButtonCap = 0
+pp_data->cap[142]->IsPadding = 0
+pp_data->cap[142]->IsAbsolute = 1
+pp_data->cap[142]->IsRange = 0
+pp_data->cap[142]->IsAlias = 0
+pp_data->cap[142]->IsStringRange = 0
+pp_data->cap[142]->IsDesignatorRange = 0
+pp_data->cap[142]->Reserved1 = 0x000000
+pp_data->cap[142]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[142]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[142]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[142]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[142]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[142]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[142]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[142]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[142]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[142]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[142]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[142]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[142]->NotRange.Usage = 0x0081
+pp_data->cap[142]->NotRange.Reserved1 = 0x0081
+pp_data->cap[142]->NotRange.StringIndex = 0
+pp_data->cap[142]->NotRange.Reserved2 = 0
+pp_data->cap[142]->NotRange.DesignatorIndex = 0
+pp_data->cap[142]->NotRange.Reserved3 = 0
+pp_data->cap[142]->NotRange.DataIndex = 55
+pp_data->cap[142]->NotRange.Reserved4 = 55
+pp_data->cap[142]->NotButton.HasNull = 0
+pp_data->cap[142]->NotButton.Reserved4 = 0x000000
+pp_data->cap[142]->NotButton.LogicalMin = 0
+pp_data->cap[142]->NotButton.LogicalMax = 127
+pp_data->cap[142]->NotButton.PhysicalMin = 0
+pp_data->cap[142]->NotButton.PhysicalMax = 0
+pp_data->cap[142]->Units = 0
+pp_data->cap[142]->UnitsExp = 0
+
+pp_data->cap[143]->UsagePage = 0xFF01
+pp_data->cap[143]->ReportID = 0x80
+pp_data->cap[143]->BitPosition = 0
+pp_data->cap[143]->BitSize = 8
+pp_data->cap[143]->ReportCount = 1
+pp_data->cap[143]->BytePosition = 0x0026
+pp_data->cap[143]->BitCount = 8
+pp_data->cap[143]->BitField = 0x02
+pp_data->cap[143]->NextBytePosition = 0x0027
+pp_data->cap[143]->LinkCollection = 0x0003
+pp_data->cap[143]->LinkUsagePage = 0xFF01
+pp_data->cap[143]->LinkUsage = 0x0080
+pp_data->cap[143]->IsMultipleItemsForArray = 0
+pp_data->cap[143]->IsButtonCap = 0
+pp_data->cap[143]->IsPadding = 0
+pp_data->cap[143]->IsAbsolute = 1
+pp_data->cap[143]->IsRange = 0
+pp_data->cap[143]->IsAlias = 0
+pp_data->cap[143]->IsStringRange = 0
+pp_data->cap[143]->IsDesignatorRange = 0
+pp_data->cap[143]->Reserved1 = 0x000000
+pp_data->cap[143]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[143]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[143]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[143]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[143]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[143]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[143]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[143]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[143]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[143]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[143]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[143]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[143]->NotRange.Usage = 0x0081
+pp_data->cap[143]->NotRange.Reserved1 = 0x0081
+pp_data->cap[143]->NotRange.StringIndex = 0
+pp_data->cap[143]->NotRange.Reserved2 = 0
+pp_data->cap[143]->NotRange.DesignatorIndex = 0
+pp_data->cap[143]->NotRange.Reserved3 = 0
+pp_data->cap[143]->NotRange.DataIndex = 56
+pp_data->cap[143]->NotRange.Reserved4 = 56
+pp_data->cap[143]->NotButton.HasNull = 0
+pp_data->cap[143]->NotButton.Reserved4 = 0x000000
+pp_data->cap[143]->NotButton.LogicalMin = 0
+pp_data->cap[143]->NotButton.LogicalMax = 127
+pp_data->cap[143]->NotButton.PhysicalMin = 0
+pp_data->cap[143]->NotButton.PhysicalMax = 0
+pp_data->cap[143]->Units = 0
+pp_data->cap[143]->UnitsExp = 0
+
+pp_data->cap[144]->UsagePage = 0xFF01
+pp_data->cap[144]->ReportID = 0x80
+pp_data->cap[144]->BitPosition = 0
+pp_data->cap[144]->BitSize = 8
+pp_data->cap[144]->ReportCount = 1
+pp_data->cap[144]->BytePosition = 0x0025
+pp_data->cap[144]->BitCount = 8
+pp_data->cap[144]->BitField = 0x02
+pp_data->cap[144]->NextBytePosition = 0x0026
+pp_data->cap[144]->LinkCollection = 0x0003
+pp_data->cap[144]->LinkUsagePage = 0xFF01
+pp_data->cap[144]->LinkUsage = 0x0080
+pp_data->cap[144]->IsMultipleItemsForArray = 0
+pp_data->cap[144]->IsButtonCap = 0
+pp_data->cap[144]->IsPadding = 0
+pp_data->cap[144]->IsAbsolute = 1
+pp_data->cap[144]->IsRange = 0
+pp_data->cap[144]->IsAlias = 0
+pp_data->cap[144]->IsStringRange = 0
+pp_data->cap[144]->IsDesignatorRange = 0
+pp_data->cap[144]->Reserved1 = 0x000000
+pp_data->cap[144]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[144]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[144]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[144]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[144]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[144]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[144]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[144]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[144]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[144]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[144]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[144]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[144]->NotRange.Usage = 0x0081
+pp_data->cap[144]->NotRange.Reserved1 = 0x0081
+pp_data->cap[144]->NotRange.StringIndex = 0
+pp_data->cap[144]->NotRange.Reserved2 = 0
+pp_data->cap[144]->NotRange.DesignatorIndex = 0
+pp_data->cap[144]->NotRange.Reserved3 = 0
+pp_data->cap[144]->NotRange.DataIndex = 57
+pp_data->cap[144]->NotRange.Reserved4 = 57
+pp_data->cap[144]->NotButton.HasNull = 0
+pp_data->cap[144]->NotButton.Reserved4 = 0x000000
+pp_data->cap[144]->NotButton.LogicalMin = 0
+pp_data->cap[144]->NotButton.LogicalMax = 127
+pp_data->cap[144]->NotButton.PhysicalMin = 0
+pp_data->cap[144]->NotButton.PhysicalMax = 0
+pp_data->cap[144]->Units = 0
+pp_data->cap[144]->UnitsExp = 0
+
+pp_data->cap[145]->UsagePage = 0xFF01
+pp_data->cap[145]->ReportID = 0x80
+pp_data->cap[145]->BitPosition = 0
+pp_data->cap[145]->BitSize = 8
+pp_data->cap[145]->ReportCount = 1
+pp_data->cap[145]->BytePosition = 0x0024
+pp_data->cap[145]->BitCount = 8
+pp_data->cap[145]->BitField = 0x02
+pp_data->cap[145]->NextBytePosition = 0x0025
+pp_data->cap[145]->LinkCollection = 0x0003
+pp_data->cap[145]->LinkUsagePage = 0xFF01
+pp_data->cap[145]->LinkUsage = 0x0080
+pp_data->cap[145]->IsMultipleItemsForArray = 0
+pp_data->cap[145]->IsButtonCap = 0
+pp_data->cap[145]->IsPadding = 0
+pp_data->cap[145]->IsAbsolute = 1
+pp_data->cap[145]->IsRange = 0
+pp_data->cap[145]->IsAlias = 0
+pp_data->cap[145]->IsStringRange = 0
+pp_data->cap[145]->IsDesignatorRange = 0
+pp_data->cap[145]->Reserved1 = 0x000000
+pp_data->cap[145]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[145]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[145]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[145]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[145]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[145]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[145]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[145]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[145]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[145]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[145]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[145]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[145]->NotRange.Usage = 0x0081
+pp_data->cap[145]->NotRange.Reserved1 = 0x0081
+pp_data->cap[145]->NotRange.StringIndex = 0
+pp_data->cap[145]->NotRange.Reserved2 = 0
+pp_data->cap[145]->NotRange.DesignatorIndex = 0
+pp_data->cap[145]->NotRange.Reserved3 = 0
+pp_data->cap[145]->NotRange.DataIndex = 58
+pp_data->cap[145]->NotRange.Reserved4 = 58
+pp_data->cap[145]->NotButton.HasNull = 0
+pp_data->cap[145]->NotButton.Reserved4 = 0x000000
+pp_data->cap[145]->NotButton.LogicalMin = 0
+pp_data->cap[145]->NotButton.LogicalMax = 127
+pp_data->cap[145]->NotButton.PhysicalMin = 0
+pp_data->cap[145]->NotButton.PhysicalMax = 0
+pp_data->cap[145]->Units = 0
+pp_data->cap[145]->UnitsExp = 0
+
+pp_data->cap[146]->UsagePage = 0xFF01
+pp_data->cap[146]->ReportID = 0x80
+pp_data->cap[146]->BitPosition = 0
+pp_data->cap[146]->BitSize = 8
+pp_data->cap[146]->ReportCount = 1
+pp_data->cap[146]->BytePosition = 0x0023
+pp_data->cap[146]->BitCount = 8
+pp_data->cap[146]->BitField = 0x02
+pp_data->cap[146]->NextBytePosition = 0x0024
+pp_data->cap[146]->LinkCollection = 0x0003
+pp_data->cap[146]->LinkUsagePage = 0xFF01
+pp_data->cap[146]->LinkUsage = 0x0080
+pp_data->cap[146]->IsMultipleItemsForArray = 0
+pp_data->cap[146]->IsButtonCap = 0
+pp_data->cap[146]->IsPadding = 0
+pp_data->cap[146]->IsAbsolute = 1
+pp_data->cap[146]->IsRange = 0
+pp_data->cap[146]->IsAlias = 0
+pp_data->cap[146]->IsStringRange = 0
+pp_data->cap[146]->IsDesignatorRange = 0
+pp_data->cap[146]->Reserved1 = 0x000000
+pp_data->cap[146]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[146]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[146]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[146]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[146]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[146]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[146]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[146]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[146]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[146]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[146]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[146]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[146]->NotRange.Usage = 0x0081
+pp_data->cap[146]->NotRange.Reserved1 = 0x0081
+pp_data->cap[146]->NotRange.StringIndex = 0
+pp_data->cap[146]->NotRange.Reserved2 = 0
+pp_data->cap[146]->NotRange.DesignatorIndex = 0
+pp_data->cap[146]->NotRange.Reserved3 = 0
+pp_data->cap[146]->NotRange.DataIndex = 59
+pp_data->cap[146]->NotRange.Reserved4 = 59
+pp_data->cap[146]->NotButton.HasNull = 0
+pp_data->cap[146]->NotButton.Reserved4 = 0x000000
+pp_data->cap[146]->NotButton.LogicalMin = 0
+pp_data->cap[146]->NotButton.LogicalMax = 127
+pp_data->cap[146]->NotButton.PhysicalMin = 0
+pp_data->cap[146]->NotButton.PhysicalMax = 0
+pp_data->cap[146]->Units = 0
+pp_data->cap[146]->UnitsExp = 0
+
+pp_data->cap[147]->UsagePage = 0xFF01
+pp_data->cap[147]->ReportID = 0x80
+pp_data->cap[147]->BitPosition = 0
+pp_data->cap[147]->BitSize = 8
+pp_data->cap[147]->ReportCount = 1
+pp_data->cap[147]->BytePosition = 0x0022
+pp_data->cap[147]->BitCount = 8
+pp_data->cap[147]->BitField = 0x02
+pp_data->cap[147]->NextBytePosition = 0x0023
+pp_data->cap[147]->LinkCollection = 0x0003
+pp_data->cap[147]->LinkUsagePage = 0xFF01
+pp_data->cap[147]->LinkUsage = 0x0080
+pp_data->cap[147]->IsMultipleItemsForArray = 0
+pp_data->cap[147]->IsButtonCap = 0
+pp_data->cap[147]->IsPadding = 0
+pp_data->cap[147]->IsAbsolute = 1
+pp_data->cap[147]->IsRange = 0
+pp_data->cap[147]->IsAlias = 0
+pp_data->cap[147]->IsStringRange = 0
+pp_data->cap[147]->IsDesignatorRange = 0
+pp_data->cap[147]->Reserved1 = 0x000000
+pp_data->cap[147]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[147]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[147]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[147]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[147]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[147]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[147]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[147]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[147]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[147]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[147]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[147]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[147]->NotRange.Usage = 0x0081
+pp_data->cap[147]->NotRange.Reserved1 = 0x0081
+pp_data->cap[147]->NotRange.StringIndex = 0
+pp_data->cap[147]->NotRange.Reserved2 = 0
+pp_data->cap[147]->NotRange.DesignatorIndex = 0
+pp_data->cap[147]->NotRange.Reserved3 = 0
+pp_data->cap[147]->NotRange.DataIndex = 60
+pp_data->cap[147]->NotRange.Reserved4 = 60
+pp_data->cap[147]->NotButton.HasNull = 0
+pp_data->cap[147]->NotButton.Reserved4 = 0x000000
+pp_data->cap[147]->NotButton.LogicalMin = 0
+pp_data->cap[147]->NotButton.LogicalMax = 127
+pp_data->cap[147]->NotButton.PhysicalMin = 0
+pp_data->cap[147]->NotButton.PhysicalMax = 0
+pp_data->cap[147]->Units = 0
+pp_data->cap[147]->UnitsExp = 0
+
+pp_data->cap[148]->UsagePage = 0xFF01
+pp_data->cap[148]->ReportID = 0x80
+pp_data->cap[148]->BitPosition = 0
+pp_data->cap[148]->BitSize = 8
+pp_data->cap[148]->ReportCount = 1
+pp_data->cap[148]->BytePosition = 0x0021
+pp_data->cap[148]->BitCount = 8
+pp_data->cap[148]->BitField = 0x02
+pp_data->cap[148]->NextBytePosition = 0x0022
+pp_data->cap[148]->LinkCollection = 0x0003
+pp_data->cap[148]->LinkUsagePage = 0xFF01
+pp_data->cap[148]->LinkUsage = 0x0080
+pp_data->cap[148]->IsMultipleItemsForArray = 0
+pp_data->cap[148]->IsButtonCap = 0
+pp_data->cap[148]->IsPadding = 0
+pp_data->cap[148]->IsAbsolute = 1
+pp_data->cap[148]->IsRange = 0
+pp_data->cap[148]->IsAlias = 0
+pp_data->cap[148]->IsStringRange = 0
+pp_data->cap[148]->IsDesignatorRange = 0
+pp_data->cap[148]->Reserved1 = 0x000000
+pp_data->cap[148]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[148]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[148]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[148]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[148]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[148]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[148]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[148]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[148]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[148]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[148]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[148]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[148]->NotRange.Usage = 0x0081
+pp_data->cap[148]->NotRange.Reserved1 = 0x0081
+pp_data->cap[148]->NotRange.StringIndex = 0
+pp_data->cap[148]->NotRange.Reserved2 = 0
+pp_data->cap[148]->NotRange.DesignatorIndex = 0
+pp_data->cap[148]->NotRange.Reserved3 = 0
+pp_data->cap[148]->NotRange.DataIndex = 61
+pp_data->cap[148]->NotRange.Reserved4 = 61
+pp_data->cap[148]->NotButton.HasNull = 0
+pp_data->cap[148]->NotButton.Reserved4 = 0x000000
+pp_data->cap[148]->NotButton.LogicalMin = 0
+pp_data->cap[148]->NotButton.LogicalMax = 127
+pp_data->cap[148]->NotButton.PhysicalMin = 0
+pp_data->cap[148]->NotButton.PhysicalMax = 0
+pp_data->cap[148]->Units = 0
+pp_data->cap[148]->UnitsExp = 0
+
+pp_data->cap[149]->UsagePage = 0xFF01
+pp_data->cap[149]->ReportID = 0x80
+pp_data->cap[149]->BitPosition = 0
+pp_data->cap[149]->BitSize = 8
+pp_data->cap[149]->ReportCount = 1
+pp_data->cap[149]->BytePosition = 0x0020
+pp_data->cap[149]->BitCount = 8
+pp_data->cap[149]->BitField = 0x02
+pp_data->cap[149]->NextBytePosition = 0x0021
+pp_data->cap[149]->LinkCollection = 0x0003
+pp_data->cap[149]->LinkUsagePage = 0xFF01
+pp_data->cap[149]->LinkUsage = 0x0080
+pp_data->cap[149]->IsMultipleItemsForArray = 0
+pp_data->cap[149]->IsButtonCap = 0
+pp_data->cap[149]->IsPadding = 0
+pp_data->cap[149]->IsAbsolute = 1
+pp_data->cap[149]->IsRange = 0
+pp_data->cap[149]->IsAlias = 0
+pp_data->cap[149]->IsStringRange = 0
+pp_data->cap[149]->IsDesignatorRange = 0
+pp_data->cap[149]->Reserved1 = 0x000000
+pp_data->cap[149]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[149]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[149]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[149]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[149]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[149]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[149]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[149]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[149]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[149]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[149]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[149]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[149]->NotRange.Usage = 0x0081
+pp_data->cap[149]->NotRange.Reserved1 = 0x0081
+pp_data->cap[149]->NotRange.StringIndex = 0
+pp_data->cap[149]->NotRange.Reserved2 = 0
+pp_data->cap[149]->NotRange.DesignatorIndex = 0
+pp_data->cap[149]->NotRange.Reserved3 = 0
+pp_data->cap[149]->NotRange.DataIndex = 62
+pp_data->cap[149]->NotRange.Reserved4 = 62
+pp_data->cap[149]->NotButton.HasNull = 0
+pp_data->cap[149]->NotButton.Reserved4 = 0x000000
+pp_data->cap[149]->NotButton.LogicalMin = 0
+pp_data->cap[149]->NotButton.LogicalMax = 127
+pp_data->cap[149]->NotButton.PhysicalMin = 0
+pp_data->cap[149]->NotButton.PhysicalMax = 0
+pp_data->cap[149]->Units = 0
+pp_data->cap[149]->UnitsExp = 0
+
+pp_data->cap[150]->UsagePage = 0xFF01
+pp_data->cap[150]->ReportID = 0x80
+pp_data->cap[150]->BitPosition = 0
+pp_data->cap[150]->BitSize = 8
+pp_data->cap[150]->ReportCount = 1
+pp_data->cap[150]->BytePosition = 0x001F
+pp_data->cap[150]->BitCount = 8
+pp_data->cap[150]->BitField = 0x02
+pp_data->cap[150]->NextBytePosition = 0x0020
+pp_data->cap[150]->LinkCollection = 0x0003
+pp_data->cap[150]->LinkUsagePage = 0xFF01
+pp_data->cap[150]->LinkUsage = 0x0080
+pp_data->cap[150]->IsMultipleItemsForArray = 0
+pp_data->cap[150]->IsButtonCap = 0
+pp_data->cap[150]->IsPadding = 0
+pp_data->cap[150]->IsAbsolute = 1
+pp_data->cap[150]->IsRange = 0
+pp_data->cap[150]->IsAlias = 0
+pp_data->cap[150]->IsStringRange = 0
+pp_data->cap[150]->IsDesignatorRange = 0
+pp_data->cap[150]->Reserved1 = 0x000000
+pp_data->cap[150]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[150]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[150]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[150]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[150]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[150]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[150]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[150]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[150]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[150]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[150]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[150]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[150]->NotRange.Usage = 0x0081
+pp_data->cap[150]->NotRange.Reserved1 = 0x0081
+pp_data->cap[150]->NotRange.StringIndex = 0
+pp_data->cap[150]->NotRange.Reserved2 = 0
+pp_data->cap[150]->NotRange.DesignatorIndex = 0
+pp_data->cap[150]->NotRange.Reserved3 = 0
+pp_data->cap[150]->NotRange.DataIndex = 63
+pp_data->cap[150]->NotRange.Reserved4 = 63
+pp_data->cap[150]->NotButton.HasNull = 0
+pp_data->cap[150]->NotButton.Reserved4 = 0x000000
+pp_data->cap[150]->NotButton.LogicalMin = 0
+pp_data->cap[150]->NotButton.LogicalMax = 127
+pp_data->cap[150]->NotButton.PhysicalMin = 0
+pp_data->cap[150]->NotButton.PhysicalMax = 0
+pp_data->cap[150]->Units = 0
+pp_data->cap[150]->UnitsExp = 0
+
+pp_data->cap[151]->UsagePage = 0xFF01
+pp_data->cap[151]->ReportID = 0x80
+pp_data->cap[151]->BitPosition = 0
+pp_data->cap[151]->BitSize = 8
+pp_data->cap[151]->ReportCount = 1
+pp_data->cap[151]->BytePosition = 0x001E
+pp_data->cap[151]->BitCount = 8
+pp_data->cap[151]->BitField = 0x02
+pp_data->cap[151]->NextBytePosition = 0x001F
+pp_data->cap[151]->LinkCollection = 0x0003
+pp_data->cap[151]->LinkUsagePage = 0xFF01
+pp_data->cap[151]->LinkUsage = 0x0080
+pp_data->cap[151]->IsMultipleItemsForArray = 0
+pp_data->cap[151]->IsButtonCap = 0
+pp_data->cap[151]->IsPadding = 0
+pp_data->cap[151]->IsAbsolute = 1
+pp_data->cap[151]->IsRange = 0
+pp_data->cap[151]->IsAlias = 0
+pp_data->cap[151]->IsStringRange = 0
+pp_data->cap[151]->IsDesignatorRange = 0
+pp_data->cap[151]->Reserved1 = 0x000000
+pp_data->cap[151]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[151]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[151]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[151]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[151]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[151]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[151]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[151]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[151]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[151]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[151]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[151]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[151]->NotRange.Usage = 0x0081
+pp_data->cap[151]->NotRange.Reserved1 = 0x0081
+pp_data->cap[151]->NotRange.StringIndex = 0
+pp_data->cap[151]->NotRange.Reserved2 = 0
+pp_data->cap[151]->NotRange.DesignatorIndex = 0
+pp_data->cap[151]->NotRange.Reserved3 = 0
+pp_data->cap[151]->NotRange.DataIndex = 64
+pp_data->cap[151]->NotRange.Reserved4 = 64
+pp_data->cap[151]->NotButton.HasNull = 0
+pp_data->cap[151]->NotButton.Reserved4 = 0x000000
+pp_data->cap[151]->NotButton.LogicalMin = 0
+pp_data->cap[151]->NotButton.LogicalMax = 127
+pp_data->cap[151]->NotButton.PhysicalMin = 0
+pp_data->cap[151]->NotButton.PhysicalMax = 0
+pp_data->cap[151]->Units = 0
+pp_data->cap[151]->UnitsExp = 0
+
+pp_data->cap[152]->UsagePage = 0xFF01
+pp_data->cap[152]->ReportID = 0x80
+pp_data->cap[152]->BitPosition = 0
+pp_data->cap[152]->BitSize = 8
+pp_data->cap[152]->ReportCount = 1
+pp_data->cap[152]->BytePosition = 0x001D
+pp_data->cap[152]->BitCount = 8
+pp_data->cap[152]->BitField = 0x02
+pp_data->cap[152]->NextBytePosition = 0x001E
+pp_data->cap[152]->LinkCollection = 0x0003
+pp_data->cap[152]->LinkUsagePage = 0xFF01
+pp_data->cap[152]->LinkUsage = 0x0080
+pp_data->cap[152]->IsMultipleItemsForArray = 0
+pp_data->cap[152]->IsButtonCap = 0
+pp_data->cap[152]->IsPadding = 0
+pp_data->cap[152]->IsAbsolute = 1
+pp_data->cap[152]->IsRange = 0
+pp_data->cap[152]->IsAlias = 0
+pp_data->cap[152]->IsStringRange = 0
+pp_data->cap[152]->IsDesignatorRange = 0
+pp_data->cap[152]->Reserved1 = 0x000000
+pp_data->cap[152]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[152]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[152]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[152]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[152]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[152]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[152]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[152]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[152]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[152]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[152]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[152]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[152]->NotRange.Usage = 0x0081
+pp_data->cap[152]->NotRange.Reserved1 = 0x0081
+pp_data->cap[152]->NotRange.StringIndex = 0
+pp_data->cap[152]->NotRange.Reserved2 = 0
+pp_data->cap[152]->NotRange.DesignatorIndex = 0
+pp_data->cap[152]->NotRange.Reserved3 = 0
+pp_data->cap[152]->NotRange.DataIndex = 65
+pp_data->cap[152]->NotRange.Reserved4 = 65
+pp_data->cap[152]->NotButton.HasNull = 0
+pp_data->cap[152]->NotButton.Reserved4 = 0x000000
+pp_data->cap[152]->NotButton.LogicalMin = 0
+pp_data->cap[152]->NotButton.LogicalMax = 127
+pp_data->cap[152]->NotButton.PhysicalMin = 0
+pp_data->cap[152]->NotButton.PhysicalMax = 0
+pp_data->cap[152]->Units = 0
+pp_data->cap[152]->UnitsExp = 0
+
+pp_data->cap[153]->UsagePage = 0xFF01
+pp_data->cap[153]->ReportID = 0x80
+pp_data->cap[153]->BitPosition = 0
+pp_data->cap[153]->BitSize = 8
+pp_data->cap[153]->ReportCount = 1
+pp_data->cap[153]->BytePosition = 0x001C
+pp_data->cap[153]->BitCount = 8
+pp_data->cap[153]->BitField = 0x02
+pp_data->cap[153]->NextBytePosition = 0x001D
+pp_data->cap[153]->LinkCollection = 0x0003
+pp_data->cap[153]->LinkUsagePage = 0xFF01
+pp_data->cap[153]->LinkUsage = 0x0080
+pp_data->cap[153]->IsMultipleItemsForArray = 0
+pp_data->cap[153]->IsButtonCap = 0
+pp_data->cap[153]->IsPadding = 0
+pp_data->cap[153]->IsAbsolute = 1
+pp_data->cap[153]->IsRange = 0
+pp_data->cap[153]->IsAlias = 0
+pp_data->cap[153]->IsStringRange = 0
+pp_data->cap[153]->IsDesignatorRange = 0
+pp_data->cap[153]->Reserved1 = 0x000000
+pp_data->cap[153]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[153]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[153]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[153]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[153]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[153]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[153]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[153]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[153]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[153]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[153]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[153]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[153]->NotRange.Usage = 0x0081
+pp_data->cap[153]->NotRange.Reserved1 = 0x0081
+pp_data->cap[153]->NotRange.StringIndex = 0
+pp_data->cap[153]->NotRange.Reserved2 = 0
+pp_data->cap[153]->NotRange.DesignatorIndex = 0
+pp_data->cap[153]->NotRange.Reserved3 = 0
+pp_data->cap[153]->NotRange.DataIndex = 66
+pp_data->cap[153]->NotRange.Reserved4 = 66
+pp_data->cap[153]->NotButton.HasNull = 0
+pp_data->cap[153]->NotButton.Reserved4 = 0x000000
+pp_data->cap[153]->NotButton.LogicalMin = 0
+pp_data->cap[153]->NotButton.LogicalMax = 127
+pp_data->cap[153]->NotButton.PhysicalMin = 0
+pp_data->cap[153]->NotButton.PhysicalMax = 0
+pp_data->cap[153]->Units = 0
+pp_data->cap[153]->UnitsExp = 0
+
+pp_data->cap[154]->UsagePage = 0xFF01
+pp_data->cap[154]->ReportID = 0x80
+pp_data->cap[154]->BitPosition = 0
+pp_data->cap[154]->BitSize = 8
+pp_data->cap[154]->ReportCount = 1
+pp_data->cap[154]->BytePosition = 0x001B
+pp_data->cap[154]->BitCount = 8
+pp_data->cap[154]->BitField = 0x02
+pp_data->cap[154]->NextBytePosition = 0x001C
+pp_data->cap[154]->LinkCollection = 0x0003
+pp_data->cap[154]->LinkUsagePage = 0xFF01
+pp_data->cap[154]->LinkUsage = 0x0080
+pp_data->cap[154]->IsMultipleItemsForArray = 0
+pp_data->cap[154]->IsButtonCap = 0
+pp_data->cap[154]->IsPadding = 0
+pp_data->cap[154]->IsAbsolute = 1
+pp_data->cap[154]->IsRange = 0
+pp_data->cap[154]->IsAlias = 0
+pp_data->cap[154]->IsStringRange = 0
+pp_data->cap[154]->IsDesignatorRange = 0
+pp_data->cap[154]->Reserved1 = 0x000000
+pp_data->cap[154]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[154]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[154]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[154]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[154]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[154]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[154]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[154]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[154]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[154]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[154]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[154]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[154]->NotRange.Usage = 0x0081
+pp_data->cap[154]->NotRange.Reserved1 = 0x0081
+pp_data->cap[154]->NotRange.StringIndex = 0
+pp_data->cap[154]->NotRange.Reserved2 = 0
+pp_data->cap[154]->NotRange.DesignatorIndex = 0
+pp_data->cap[154]->NotRange.Reserved3 = 0
+pp_data->cap[154]->NotRange.DataIndex = 67
+pp_data->cap[154]->NotRange.Reserved4 = 67
+pp_data->cap[154]->NotButton.HasNull = 0
+pp_data->cap[154]->NotButton.Reserved4 = 0x000000
+pp_data->cap[154]->NotButton.LogicalMin = 0
+pp_data->cap[154]->NotButton.LogicalMax = 127
+pp_data->cap[154]->NotButton.PhysicalMin = 0
+pp_data->cap[154]->NotButton.PhysicalMax = 0
+pp_data->cap[154]->Units = 0
+pp_data->cap[154]->UnitsExp = 0
+
+pp_data->cap[155]->UsagePage = 0xFF01
+pp_data->cap[155]->ReportID = 0x80
+pp_data->cap[155]->BitPosition = 0
+pp_data->cap[155]->BitSize = 8
+pp_data->cap[155]->ReportCount = 1
+pp_data->cap[155]->BytePosition = 0x001A
+pp_data->cap[155]->BitCount = 8
+pp_data->cap[155]->BitField = 0x02
+pp_data->cap[155]->NextBytePosition = 0x001B
+pp_data->cap[155]->LinkCollection = 0x0003
+pp_data->cap[155]->LinkUsagePage = 0xFF01
+pp_data->cap[155]->LinkUsage = 0x0080
+pp_data->cap[155]->IsMultipleItemsForArray = 0
+pp_data->cap[155]->IsButtonCap = 0
+pp_data->cap[155]->IsPadding = 0
+pp_data->cap[155]->IsAbsolute = 1
+pp_data->cap[155]->IsRange = 0
+pp_data->cap[155]->IsAlias = 0
+pp_data->cap[155]->IsStringRange = 0
+pp_data->cap[155]->IsDesignatorRange = 0
+pp_data->cap[155]->Reserved1 = 0x000000
+pp_data->cap[155]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[155]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[155]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[155]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[155]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[155]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[155]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[155]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[155]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[155]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[155]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[155]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[155]->NotRange.Usage = 0x0081
+pp_data->cap[155]->NotRange.Reserved1 = 0x0081
+pp_data->cap[155]->NotRange.StringIndex = 0
+pp_data->cap[155]->NotRange.Reserved2 = 0
+pp_data->cap[155]->NotRange.DesignatorIndex = 0
+pp_data->cap[155]->NotRange.Reserved3 = 0
+pp_data->cap[155]->NotRange.DataIndex = 68
+pp_data->cap[155]->NotRange.Reserved4 = 68
+pp_data->cap[155]->NotButton.HasNull = 0
+pp_data->cap[155]->NotButton.Reserved4 = 0x000000
+pp_data->cap[155]->NotButton.LogicalMin = 0
+pp_data->cap[155]->NotButton.LogicalMax = 127
+pp_data->cap[155]->NotButton.PhysicalMin = 0
+pp_data->cap[155]->NotButton.PhysicalMax = 0
+pp_data->cap[155]->Units = 0
+pp_data->cap[155]->UnitsExp = 0
+
+pp_data->cap[156]->UsagePage = 0xFF01
+pp_data->cap[156]->ReportID = 0x80
+pp_data->cap[156]->BitPosition = 0
+pp_data->cap[156]->BitSize = 8
+pp_data->cap[156]->ReportCount = 1
+pp_data->cap[156]->BytePosition = 0x0019
+pp_data->cap[156]->BitCount = 8
+pp_data->cap[156]->BitField = 0x02
+pp_data->cap[156]->NextBytePosition = 0x001A
+pp_data->cap[156]->LinkCollection = 0x0003
+pp_data->cap[156]->LinkUsagePage = 0xFF01
+pp_data->cap[156]->LinkUsage = 0x0080
+pp_data->cap[156]->IsMultipleItemsForArray = 0
+pp_data->cap[156]->IsButtonCap = 0
+pp_data->cap[156]->IsPadding = 0
+pp_data->cap[156]->IsAbsolute = 1
+pp_data->cap[156]->IsRange = 0
+pp_data->cap[156]->IsAlias = 0
+pp_data->cap[156]->IsStringRange = 0
+pp_data->cap[156]->IsDesignatorRange = 0
+pp_data->cap[156]->Reserved1 = 0x000000
+pp_data->cap[156]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[156]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[156]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[156]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[156]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[156]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[156]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[156]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[156]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[156]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[156]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[156]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[156]->NotRange.Usage = 0x0081
+pp_data->cap[156]->NotRange.Reserved1 = 0x0081
+pp_data->cap[156]->NotRange.StringIndex = 0
+pp_data->cap[156]->NotRange.Reserved2 = 0
+pp_data->cap[156]->NotRange.DesignatorIndex = 0
+pp_data->cap[156]->NotRange.Reserved3 = 0
+pp_data->cap[156]->NotRange.DataIndex = 69
+pp_data->cap[156]->NotRange.Reserved4 = 69
+pp_data->cap[156]->NotButton.HasNull = 0
+pp_data->cap[156]->NotButton.Reserved4 = 0x000000
+pp_data->cap[156]->NotButton.LogicalMin = 0
+pp_data->cap[156]->NotButton.LogicalMax = 127
+pp_data->cap[156]->NotButton.PhysicalMin = 0
+pp_data->cap[156]->NotButton.PhysicalMax = 0
+pp_data->cap[156]->Units = 0
+pp_data->cap[156]->UnitsExp = 0
+
+pp_data->cap[157]->UsagePage = 0xFF01
+pp_data->cap[157]->ReportID = 0x80
+pp_data->cap[157]->BitPosition = 0
+pp_data->cap[157]->BitSize = 8
+pp_data->cap[157]->ReportCount = 1
+pp_data->cap[157]->BytePosition = 0x0018
+pp_data->cap[157]->BitCount = 8
+pp_data->cap[157]->BitField = 0x02
+pp_data->cap[157]->NextBytePosition = 0x0019
+pp_data->cap[157]->LinkCollection = 0x0003
+pp_data->cap[157]->LinkUsagePage = 0xFF01
+pp_data->cap[157]->LinkUsage = 0x0080
+pp_data->cap[157]->IsMultipleItemsForArray = 0
+pp_data->cap[157]->IsButtonCap = 0
+pp_data->cap[157]->IsPadding = 0
+pp_data->cap[157]->IsAbsolute = 1
+pp_data->cap[157]->IsRange = 0
+pp_data->cap[157]->IsAlias = 0
+pp_data->cap[157]->IsStringRange = 0
+pp_data->cap[157]->IsDesignatorRange = 0
+pp_data->cap[157]->Reserved1 = 0x000000
+pp_data->cap[157]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[157]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[157]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[157]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[157]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[157]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[157]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[157]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[157]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[157]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[157]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[157]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[157]->NotRange.Usage = 0x0081
+pp_data->cap[157]->NotRange.Reserved1 = 0x0081
+pp_data->cap[157]->NotRange.StringIndex = 0
+pp_data->cap[157]->NotRange.Reserved2 = 0
+pp_data->cap[157]->NotRange.DesignatorIndex = 0
+pp_data->cap[157]->NotRange.Reserved3 = 0
+pp_data->cap[157]->NotRange.DataIndex = 70
+pp_data->cap[157]->NotRange.Reserved4 = 70
+pp_data->cap[157]->NotButton.HasNull = 0
+pp_data->cap[157]->NotButton.Reserved4 = 0x000000
+pp_data->cap[157]->NotButton.LogicalMin = 0
+pp_data->cap[157]->NotButton.LogicalMax = 127
+pp_data->cap[157]->NotButton.PhysicalMin = 0
+pp_data->cap[157]->NotButton.PhysicalMax = 0
+pp_data->cap[157]->Units = 0
+pp_data->cap[157]->UnitsExp = 0
+
+pp_data->cap[158]->UsagePage = 0xFF01
+pp_data->cap[158]->ReportID = 0x80
+pp_data->cap[158]->BitPosition = 0
+pp_data->cap[158]->BitSize = 8
+pp_data->cap[158]->ReportCount = 1
+pp_data->cap[158]->BytePosition = 0x0017
+pp_data->cap[158]->BitCount = 8
+pp_data->cap[158]->BitField = 0x02
+pp_data->cap[158]->NextBytePosition = 0x0018
+pp_data->cap[158]->LinkCollection = 0x0003
+pp_data->cap[158]->LinkUsagePage = 0xFF01
+pp_data->cap[158]->LinkUsage = 0x0080
+pp_data->cap[158]->IsMultipleItemsForArray = 0
+pp_data->cap[158]->IsButtonCap = 0
+pp_data->cap[158]->IsPadding = 0
+pp_data->cap[158]->IsAbsolute = 1
+pp_data->cap[158]->IsRange = 0
+pp_data->cap[158]->IsAlias = 0
+pp_data->cap[158]->IsStringRange = 0
+pp_data->cap[158]->IsDesignatorRange = 0
+pp_data->cap[158]->Reserved1 = 0x000000
+pp_data->cap[158]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[158]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[158]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[158]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[158]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[158]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[158]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[158]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[158]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[158]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[158]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[158]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[158]->NotRange.Usage = 0x0081
+pp_data->cap[158]->NotRange.Reserved1 = 0x0081
+pp_data->cap[158]->NotRange.StringIndex = 0
+pp_data->cap[158]->NotRange.Reserved2 = 0
+pp_data->cap[158]->NotRange.DesignatorIndex = 0
+pp_data->cap[158]->NotRange.Reserved3 = 0
+pp_data->cap[158]->NotRange.DataIndex = 71
+pp_data->cap[158]->NotRange.Reserved4 = 71
+pp_data->cap[158]->NotButton.HasNull = 0
+pp_data->cap[158]->NotButton.Reserved4 = 0x000000
+pp_data->cap[158]->NotButton.LogicalMin = 0
+pp_data->cap[158]->NotButton.LogicalMax = 127
+pp_data->cap[158]->NotButton.PhysicalMin = 0
+pp_data->cap[158]->NotButton.PhysicalMax = 0
+pp_data->cap[158]->Units = 0
+pp_data->cap[158]->UnitsExp = 0
+
+pp_data->cap[159]->UsagePage = 0xFF01
+pp_data->cap[159]->ReportID = 0x80
+pp_data->cap[159]->BitPosition = 0
+pp_data->cap[159]->BitSize = 8
+pp_data->cap[159]->ReportCount = 1
+pp_data->cap[159]->BytePosition = 0x0016
+pp_data->cap[159]->BitCount = 8
+pp_data->cap[159]->BitField = 0x02
+pp_data->cap[159]->NextBytePosition = 0x0017
+pp_data->cap[159]->LinkCollection = 0x0003
+pp_data->cap[159]->LinkUsagePage = 0xFF01
+pp_data->cap[159]->LinkUsage = 0x0080
+pp_data->cap[159]->IsMultipleItemsForArray = 0
+pp_data->cap[159]->IsButtonCap = 0
+pp_data->cap[159]->IsPadding = 0
+pp_data->cap[159]->IsAbsolute = 1
+pp_data->cap[159]->IsRange = 0
+pp_data->cap[159]->IsAlias = 0
+pp_data->cap[159]->IsStringRange = 0
+pp_data->cap[159]->IsDesignatorRange = 0
+pp_data->cap[159]->Reserved1 = 0x000000
+pp_data->cap[159]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[159]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[159]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[159]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[159]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[159]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[159]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[159]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[159]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[159]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[159]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[159]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[159]->NotRange.Usage = 0x0081
+pp_data->cap[159]->NotRange.Reserved1 = 0x0081
+pp_data->cap[159]->NotRange.StringIndex = 0
+pp_data->cap[159]->NotRange.Reserved2 = 0
+pp_data->cap[159]->NotRange.DesignatorIndex = 0
+pp_data->cap[159]->NotRange.Reserved3 = 0
+pp_data->cap[159]->NotRange.DataIndex = 72
+pp_data->cap[159]->NotRange.Reserved4 = 72
+pp_data->cap[159]->NotButton.HasNull = 0
+pp_data->cap[159]->NotButton.Reserved4 = 0x000000
+pp_data->cap[159]->NotButton.LogicalMin = 0
+pp_data->cap[159]->NotButton.LogicalMax = 127
+pp_data->cap[159]->NotButton.PhysicalMin = 0
+pp_data->cap[159]->NotButton.PhysicalMax = 0
+pp_data->cap[159]->Units = 0
+pp_data->cap[159]->UnitsExp = 0
+
+pp_data->cap[160]->UsagePage = 0xFF01
+pp_data->cap[160]->ReportID = 0x80
+pp_data->cap[160]->BitPosition = 0
+pp_data->cap[160]->BitSize = 8
+pp_data->cap[160]->ReportCount = 1
+pp_data->cap[160]->BytePosition = 0x0015
+pp_data->cap[160]->BitCount = 8
+pp_data->cap[160]->BitField = 0x02
+pp_data->cap[160]->NextBytePosition = 0x0016
+pp_data->cap[160]->LinkCollection = 0x0003
+pp_data->cap[160]->LinkUsagePage = 0xFF01
+pp_data->cap[160]->LinkUsage = 0x0080
+pp_data->cap[160]->IsMultipleItemsForArray = 0
+pp_data->cap[160]->IsButtonCap = 0
+pp_data->cap[160]->IsPadding = 0
+pp_data->cap[160]->IsAbsolute = 1
+pp_data->cap[160]->IsRange = 0
+pp_data->cap[160]->IsAlias = 0
+pp_data->cap[160]->IsStringRange = 0
+pp_data->cap[160]->IsDesignatorRange = 0
+pp_data->cap[160]->Reserved1 = 0x000000
+pp_data->cap[160]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[160]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[160]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[160]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[160]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[160]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[160]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[160]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[160]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[160]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[160]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[160]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[160]->NotRange.Usage = 0x0081
+pp_data->cap[160]->NotRange.Reserved1 = 0x0081
+pp_data->cap[160]->NotRange.StringIndex = 0
+pp_data->cap[160]->NotRange.Reserved2 = 0
+pp_data->cap[160]->NotRange.DesignatorIndex = 0
+pp_data->cap[160]->NotRange.Reserved3 = 0
+pp_data->cap[160]->NotRange.DataIndex = 73
+pp_data->cap[160]->NotRange.Reserved4 = 73
+pp_data->cap[160]->NotButton.HasNull = 0
+pp_data->cap[160]->NotButton.Reserved4 = 0x000000
+pp_data->cap[160]->NotButton.LogicalMin = 0
+pp_data->cap[160]->NotButton.LogicalMax = 127
+pp_data->cap[160]->NotButton.PhysicalMin = 0
+pp_data->cap[160]->NotButton.PhysicalMax = 0
+pp_data->cap[160]->Units = 0
+pp_data->cap[160]->UnitsExp = 0
+
+pp_data->cap[161]->UsagePage = 0xFF01
+pp_data->cap[161]->ReportID = 0x80
+pp_data->cap[161]->BitPosition = 0
+pp_data->cap[161]->BitSize = 8
+pp_data->cap[161]->ReportCount = 1
+pp_data->cap[161]->BytePosition = 0x0014
+pp_data->cap[161]->BitCount = 8
+pp_data->cap[161]->BitField = 0x02
+pp_data->cap[161]->NextBytePosition = 0x0015
+pp_data->cap[161]->LinkCollection = 0x0003
+pp_data->cap[161]->LinkUsagePage = 0xFF01
+pp_data->cap[161]->LinkUsage = 0x0080
+pp_data->cap[161]->IsMultipleItemsForArray = 0
+pp_data->cap[161]->IsButtonCap = 0
+pp_data->cap[161]->IsPadding = 0
+pp_data->cap[161]->IsAbsolute = 1
+pp_data->cap[161]->IsRange = 0
+pp_data->cap[161]->IsAlias = 0
+pp_data->cap[161]->IsStringRange = 0
+pp_data->cap[161]->IsDesignatorRange = 0
+pp_data->cap[161]->Reserved1 = 0x000000
+pp_data->cap[161]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[161]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[161]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[161]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[161]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[161]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[161]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[161]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[161]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[161]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[161]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[161]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[161]->NotRange.Usage = 0x0081
+pp_data->cap[161]->NotRange.Reserved1 = 0x0081
+pp_data->cap[161]->NotRange.StringIndex = 0
+pp_data->cap[161]->NotRange.Reserved2 = 0
+pp_data->cap[161]->NotRange.DesignatorIndex = 0
+pp_data->cap[161]->NotRange.Reserved3 = 0
+pp_data->cap[161]->NotRange.DataIndex = 74
+pp_data->cap[161]->NotRange.Reserved4 = 74
+pp_data->cap[161]->NotButton.HasNull = 0
+pp_data->cap[161]->NotButton.Reserved4 = 0x000000
+pp_data->cap[161]->NotButton.LogicalMin = 0
+pp_data->cap[161]->NotButton.LogicalMax = 127
+pp_data->cap[161]->NotButton.PhysicalMin = 0
+pp_data->cap[161]->NotButton.PhysicalMax = 0
+pp_data->cap[161]->Units = 0
+pp_data->cap[161]->UnitsExp = 0
+
+pp_data->cap[162]->UsagePage = 0xFF01
+pp_data->cap[162]->ReportID = 0x80
+pp_data->cap[162]->BitPosition = 0
+pp_data->cap[162]->BitSize = 8
+pp_data->cap[162]->ReportCount = 1
+pp_data->cap[162]->BytePosition = 0x0013
+pp_data->cap[162]->BitCount = 8
+pp_data->cap[162]->BitField = 0x02
+pp_data->cap[162]->NextBytePosition = 0x0014
+pp_data->cap[162]->LinkCollection = 0x0003
+pp_data->cap[162]->LinkUsagePage = 0xFF01
+pp_data->cap[162]->LinkUsage = 0x0080
+pp_data->cap[162]->IsMultipleItemsForArray = 0
+pp_data->cap[162]->IsButtonCap = 0
+pp_data->cap[162]->IsPadding = 0
+pp_data->cap[162]->IsAbsolute = 1
+pp_data->cap[162]->IsRange = 0
+pp_data->cap[162]->IsAlias = 0
+pp_data->cap[162]->IsStringRange = 0
+pp_data->cap[162]->IsDesignatorRange = 0
+pp_data->cap[162]->Reserved1 = 0x000000
+pp_data->cap[162]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[162]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[162]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[162]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[162]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[162]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[162]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[162]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[162]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[162]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[162]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[162]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[162]->NotRange.Usage = 0x0081
+pp_data->cap[162]->NotRange.Reserved1 = 0x0081
+pp_data->cap[162]->NotRange.StringIndex = 0
+pp_data->cap[162]->NotRange.Reserved2 = 0
+pp_data->cap[162]->NotRange.DesignatorIndex = 0
+pp_data->cap[162]->NotRange.Reserved3 = 0
+pp_data->cap[162]->NotRange.DataIndex = 75
+pp_data->cap[162]->NotRange.Reserved4 = 75
+pp_data->cap[162]->NotButton.HasNull = 0
+pp_data->cap[162]->NotButton.Reserved4 = 0x000000
+pp_data->cap[162]->NotButton.LogicalMin = 0
+pp_data->cap[162]->NotButton.LogicalMax = 127
+pp_data->cap[162]->NotButton.PhysicalMin = 0
+pp_data->cap[162]->NotButton.PhysicalMax = 0
+pp_data->cap[162]->Units = 0
+pp_data->cap[162]->UnitsExp = 0
+
+pp_data->cap[163]->UsagePage = 0xFF01
+pp_data->cap[163]->ReportID = 0x80
+pp_data->cap[163]->BitPosition = 0
+pp_data->cap[163]->BitSize = 8
+pp_data->cap[163]->ReportCount = 1
+pp_data->cap[163]->BytePosition = 0x0012
+pp_data->cap[163]->BitCount = 8
+pp_data->cap[163]->BitField = 0x02
+pp_data->cap[163]->NextBytePosition = 0x0013
+pp_data->cap[163]->LinkCollection = 0x0003
+pp_data->cap[163]->LinkUsagePage = 0xFF01
+pp_data->cap[163]->LinkUsage = 0x0080
+pp_data->cap[163]->IsMultipleItemsForArray = 0
+pp_data->cap[163]->IsButtonCap = 0
+pp_data->cap[163]->IsPadding = 0
+pp_data->cap[163]->IsAbsolute = 1
+pp_data->cap[163]->IsRange = 0
+pp_data->cap[163]->IsAlias = 0
+pp_data->cap[163]->IsStringRange = 0
+pp_data->cap[163]->IsDesignatorRange = 0
+pp_data->cap[163]->Reserved1 = 0x000000
+pp_data->cap[163]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[163]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[163]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[163]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[163]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[163]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[163]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[163]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[163]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[163]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[163]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[163]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[163]->NotRange.Usage = 0x0081
+pp_data->cap[163]->NotRange.Reserved1 = 0x0081
+pp_data->cap[163]->NotRange.StringIndex = 0
+pp_data->cap[163]->NotRange.Reserved2 = 0
+pp_data->cap[163]->NotRange.DesignatorIndex = 0
+pp_data->cap[163]->NotRange.Reserved3 = 0
+pp_data->cap[163]->NotRange.DataIndex = 76
+pp_data->cap[163]->NotRange.Reserved4 = 76
+pp_data->cap[163]->NotButton.HasNull = 0
+pp_data->cap[163]->NotButton.Reserved4 = 0x000000
+pp_data->cap[163]->NotButton.LogicalMin = 0
+pp_data->cap[163]->NotButton.LogicalMax = 127
+pp_data->cap[163]->NotButton.PhysicalMin = 0
+pp_data->cap[163]->NotButton.PhysicalMax = 0
+pp_data->cap[163]->Units = 0
+pp_data->cap[163]->UnitsExp = 0
+
+pp_data->cap[164]->UsagePage = 0xFF01
+pp_data->cap[164]->ReportID = 0x80
+pp_data->cap[164]->BitPosition = 0
+pp_data->cap[164]->BitSize = 8
+pp_data->cap[164]->ReportCount = 1
+pp_data->cap[164]->BytePosition = 0x0011
+pp_data->cap[164]->BitCount = 8
+pp_data->cap[164]->BitField = 0x02
+pp_data->cap[164]->NextBytePosition = 0x0012
+pp_data->cap[164]->LinkCollection = 0x0003
+pp_data->cap[164]->LinkUsagePage = 0xFF01
+pp_data->cap[164]->LinkUsage = 0x0080
+pp_data->cap[164]->IsMultipleItemsForArray = 0
+pp_data->cap[164]->IsButtonCap = 0
+pp_data->cap[164]->IsPadding = 0
+pp_data->cap[164]->IsAbsolute = 1
+pp_data->cap[164]->IsRange = 0
+pp_data->cap[164]->IsAlias = 0
+pp_data->cap[164]->IsStringRange = 0
+pp_data->cap[164]->IsDesignatorRange = 0
+pp_data->cap[164]->Reserved1 = 0x000000
+pp_data->cap[164]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[164]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[164]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[164]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[164]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[164]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[164]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[164]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[164]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[164]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[164]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[164]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[164]->NotRange.Usage = 0x0081
+pp_data->cap[164]->NotRange.Reserved1 = 0x0081
+pp_data->cap[164]->NotRange.StringIndex = 0
+pp_data->cap[164]->NotRange.Reserved2 = 0
+pp_data->cap[164]->NotRange.DesignatorIndex = 0
+pp_data->cap[164]->NotRange.Reserved3 = 0
+pp_data->cap[164]->NotRange.DataIndex = 77
+pp_data->cap[164]->NotRange.Reserved4 = 77
+pp_data->cap[164]->NotButton.HasNull = 0
+pp_data->cap[164]->NotButton.Reserved4 = 0x000000
+pp_data->cap[164]->NotButton.LogicalMin = 0
+pp_data->cap[164]->NotButton.LogicalMax = 127
+pp_data->cap[164]->NotButton.PhysicalMin = 0
+pp_data->cap[164]->NotButton.PhysicalMax = 0
+pp_data->cap[164]->Units = 0
+pp_data->cap[164]->UnitsExp = 0
+
+pp_data->cap[165]->UsagePage = 0xFF01
+pp_data->cap[165]->ReportID = 0x80
+pp_data->cap[165]->BitPosition = 0
+pp_data->cap[165]->BitSize = 8
+pp_data->cap[165]->ReportCount = 1
+pp_data->cap[165]->BytePosition = 0x0010
+pp_data->cap[165]->BitCount = 8
+pp_data->cap[165]->BitField = 0x02
+pp_data->cap[165]->NextBytePosition = 0x0011
+pp_data->cap[165]->LinkCollection = 0x0003
+pp_data->cap[165]->LinkUsagePage = 0xFF01
+pp_data->cap[165]->LinkUsage = 0x0080
+pp_data->cap[165]->IsMultipleItemsForArray = 0
+pp_data->cap[165]->IsButtonCap = 0
+pp_data->cap[165]->IsPadding = 0
+pp_data->cap[165]->IsAbsolute = 1
+pp_data->cap[165]->IsRange = 0
+pp_data->cap[165]->IsAlias = 0
+pp_data->cap[165]->IsStringRange = 0
+pp_data->cap[165]->IsDesignatorRange = 0
+pp_data->cap[165]->Reserved1 = 0x000000
+pp_data->cap[165]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[165]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[165]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[165]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[165]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[165]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[165]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[165]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[165]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[165]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[165]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[165]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[165]->NotRange.Usage = 0x0081
+pp_data->cap[165]->NotRange.Reserved1 = 0x0081
+pp_data->cap[165]->NotRange.StringIndex = 0
+pp_data->cap[165]->NotRange.Reserved2 = 0
+pp_data->cap[165]->NotRange.DesignatorIndex = 0
+pp_data->cap[165]->NotRange.Reserved3 = 0
+pp_data->cap[165]->NotRange.DataIndex = 78
+pp_data->cap[165]->NotRange.Reserved4 = 78
+pp_data->cap[165]->NotButton.HasNull = 0
+pp_data->cap[165]->NotButton.Reserved4 = 0x000000
+pp_data->cap[165]->NotButton.LogicalMin = 0
+pp_data->cap[165]->NotButton.LogicalMax = 127
+pp_data->cap[165]->NotButton.PhysicalMin = 0
+pp_data->cap[165]->NotButton.PhysicalMax = 0
+pp_data->cap[165]->Units = 0
+pp_data->cap[165]->UnitsExp = 0
+
+pp_data->cap[166]->UsagePage = 0xFF01
+pp_data->cap[166]->ReportID = 0x80
+pp_data->cap[166]->BitPosition = 0
+pp_data->cap[166]->BitSize = 8
+pp_data->cap[166]->ReportCount = 1
+pp_data->cap[166]->BytePosition = 0x000F
+pp_data->cap[166]->BitCount = 8
+pp_data->cap[166]->BitField = 0x02
+pp_data->cap[166]->NextBytePosition = 0x0010
+pp_data->cap[166]->LinkCollection = 0x0003
+pp_data->cap[166]->LinkUsagePage = 0xFF01
+pp_data->cap[166]->LinkUsage = 0x0080
+pp_data->cap[166]->IsMultipleItemsForArray = 0
+pp_data->cap[166]->IsButtonCap = 0
+pp_data->cap[166]->IsPadding = 0
+pp_data->cap[166]->IsAbsolute = 1
+pp_data->cap[166]->IsRange = 0
+pp_data->cap[166]->IsAlias = 0
+pp_data->cap[166]->IsStringRange = 0
+pp_data->cap[166]->IsDesignatorRange = 0
+pp_data->cap[166]->Reserved1 = 0x000000
+pp_data->cap[166]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[166]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[166]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[166]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[166]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[166]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[166]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[166]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[166]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[166]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[166]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[166]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[166]->NotRange.Usage = 0x0081
+pp_data->cap[166]->NotRange.Reserved1 = 0x0081
+pp_data->cap[166]->NotRange.StringIndex = 0
+pp_data->cap[166]->NotRange.Reserved2 = 0
+pp_data->cap[166]->NotRange.DesignatorIndex = 0
+pp_data->cap[166]->NotRange.Reserved3 = 0
+pp_data->cap[166]->NotRange.DataIndex = 79
+pp_data->cap[166]->NotRange.Reserved4 = 79
+pp_data->cap[166]->NotButton.HasNull = 0
+pp_data->cap[166]->NotButton.Reserved4 = 0x000000
+pp_data->cap[166]->NotButton.LogicalMin = 0
+pp_data->cap[166]->NotButton.LogicalMax = 127
+pp_data->cap[166]->NotButton.PhysicalMin = 0
+pp_data->cap[166]->NotButton.PhysicalMax = 0
+pp_data->cap[166]->Units = 0
+pp_data->cap[166]->UnitsExp = 0
+
+pp_data->cap[167]->UsagePage = 0xFF01
+pp_data->cap[167]->ReportID = 0x80
+pp_data->cap[167]->BitPosition = 0
+pp_data->cap[167]->BitSize = 8
+pp_data->cap[167]->ReportCount = 1
+pp_data->cap[167]->BytePosition = 0x000E
+pp_data->cap[167]->BitCount = 8
+pp_data->cap[167]->BitField = 0x02
+pp_data->cap[167]->NextBytePosition = 0x000F
+pp_data->cap[167]->LinkCollection = 0x0003
+pp_data->cap[167]->LinkUsagePage = 0xFF01
+pp_data->cap[167]->LinkUsage = 0x0080
+pp_data->cap[167]->IsMultipleItemsForArray = 0
+pp_data->cap[167]->IsButtonCap = 0
+pp_data->cap[167]->IsPadding = 0
+pp_data->cap[167]->IsAbsolute = 1
+pp_data->cap[167]->IsRange = 0
+pp_data->cap[167]->IsAlias = 0
+pp_data->cap[167]->IsStringRange = 0
+pp_data->cap[167]->IsDesignatorRange = 0
+pp_data->cap[167]->Reserved1 = 0x000000
+pp_data->cap[167]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[167]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[167]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[167]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[167]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[167]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[167]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[167]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[167]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[167]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[167]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[167]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[167]->NotRange.Usage = 0x0081
+pp_data->cap[167]->NotRange.Reserved1 = 0x0081
+pp_data->cap[167]->NotRange.StringIndex = 0
+pp_data->cap[167]->NotRange.Reserved2 = 0
+pp_data->cap[167]->NotRange.DesignatorIndex = 0
+pp_data->cap[167]->NotRange.Reserved3 = 0
+pp_data->cap[167]->NotRange.DataIndex = 80
+pp_data->cap[167]->NotRange.Reserved4 = 80
+pp_data->cap[167]->NotButton.HasNull = 0
+pp_data->cap[167]->NotButton.Reserved4 = 0x000000
+pp_data->cap[167]->NotButton.LogicalMin = 0
+pp_data->cap[167]->NotButton.LogicalMax = 127
+pp_data->cap[167]->NotButton.PhysicalMin = 0
+pp_data->cap[167]->NotButton.PhysicalMax = 0
+pp_data->cap[167]->Units = 0
+pp_data->cap[167]->UnitsExp = 0
+
+pp_data->cap[168]->UsagePage = 0xFF01
+pp_data->cap[168]->ReportID = 0x80
+pp_data->cap[168]->BitPosition = 0
+pp_data->cap[168]->BitSize = 8
+pp_data->cap[168]->ReportCount = 1
+pp_data->cap[168]->BytePosition = 0x000D
+pp_data->cap[168]->BitCount = 8
+pp_data->cap[168]->BitField = 0x02
+pp_data->cap[168]->NextBytePosition = 0x000E
+pp_data->cap[168]->LinkCollection = 0x0003
+pp_data->cap[168]->LinkUsagePage = 0xFF01
+pp_data->cap[168]->LinkUsage = 0x0080
+pp_data->cap[168]->IsMultipleItemsForArray = 0
+pp_data->cap[168]->IsButtonCap = 0
+pp_data->cap[168]->IsPadding = 0
+pp_data->cap[168]->IsAbsolute = 1
+pp_data->cap[168]->IsRange = 0
+pp_data->cap[168]->IsAlias = 0
+pp_data->cap[168]->IsStringRange = 0
+pp_data->cap[168]->IsDesignatorRange = 0
+pp_data->cap[168]->Reserved1 = 0x000000
+pp_data->cap[168]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[168]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[168]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[168]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[168]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[168]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[168]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[168]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[168]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[168]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[168]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[168]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[168]->NotRange.Usage = 0x0081
+pp_data->cap[168]->NotRange.Reserved1 = 0x0081
+pp_data->cap[168]->NotRange.StringIndex = 0
+pp_data->cap[168]->NotRange.Reserved2 = 0
+pp_data->cap[168]->NotRange.DesignatorIndex = 0
+pp_data->cap[168]->NotRange.Reserved3 = 0
+pp_data->cap[168]->NotRange.DataIndex = 81
+pp_data->cap[168]->NotRange.Reserved4 = 81
+pp_data->cap[168]->NotButton.HasNull = 0
+pp_data->cap[168]->NotButton.Reserved4 = 0x000000
+pp_data->cap[168]->NotButton.LogicalMin = 0
+pp_data->cap[168]->NotButton.LogicalMax = 127
+pp_data->cap[168]->NotButton.PhysicalMin = 0
+pp_data->cap[168]->NotButton.PhysicalMax = 0
+pp_data->cap[168]->Units = 0
+pp_data->cap[168]->UnitsExp = 0
+
+pp_data->cap[169]->UsagePage = 0xFF01
+pp_data->cap[169]->ReportID = 0x80
+pp_data->cap[169]->BitPosition = 0
+pp_data->cap[169]->BitSize = 8
+pp_data->cap[169]->ReportCount = 1
+pp_data->cap[169]->BytePosition = 0x000C
+pp_data->cap[169]->BitCount = 8
+pp_data->cap[169]->BitField = 0x02
+pp_data->cap[169]->NextBytePosition = 0x000D
+pp_data->cap[169]->LinkCollection = 0x0003
+pp_data->cap[169]->LinkUsagePage = 0xFF01
+pp_data->cap[169]->LinkUsage = 0x0080
+pp_data->cap[169]->IsMultipleItemsForArray = 0
+pp_data->cap[169]->IsButtonCap = 0
+pp_data->cap[169]->IsPadding = 0
+pp_data->cap[169]->IsAbsolute = 1
+pp_data->cap[169]->IsRange = 0
+pp_data->cap[169]->IsAlias = 0
+pp_data->cap[169]->IsStringRange = 0
+pp_data->cap[169]->IsDesignatorRange = 0
+pp_data->cap[169]->Reserved1 = 0x000000
+pp_data->cap[169]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[169]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[169]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[169]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[169]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[169]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[169]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[169]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[169]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[169]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[169]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[169]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[169]->NotRange.Usage = 0x0081
+pp_data->cap[169]->NotRange.Reserved1 = 0x0081
+pp_data->cap[169]->NotRange.StringIndex = 0
+pp_data->cap[169]->NotRange.Reserved2 = 0
+pp_data->cap[169]->NotRange.DesignatorIndex = 0
+pp_data->cap[169]->NotRange.Reserved3 = 0
+pp_data->cap[169]->NotRange.DataIndex = 82
+pp_data->cap[169]->NotRange.Reserved4 = 82
+pp_data->cap[169]->NotButton.HasNull = 0
+pp_data->cap[169]->NotButton.Reserved4 = 0x000000
+pp_data->cap[169]->NotButton.LogicalMin = 0
+pp_data->cap[169]->NotButton.LogicalMax = 127
+pp_data->cap[169]->NotButton.PhysicalMin = 0
+pp_data->cap[169]->NotButton.PhysicalMax = 0
+pp_data->cap[169]->Units = 0
+pp_data->cap[169]->UnitsExp = 0
+
+pp_data->cap[170]->UsagePage = 0xFF01
+pp_data->cap[170]->ReportID = 0x80
+pp_data->cap[170]->BitPosition = 0
+pp_data->cap[170]->BitSize = 8
+pp_data->cap[170]->ReportCount = 1
+pp_data->cap[170]->BytePosition = 0x000B
+pp_data->cap[170]->BitCount = 8
+pp_data->cap[170]->BitField = 0x02
+pp_data->cap[170]->NextBytePosition = 0x000C
+pp_data->cap[170]->LinkCollection = 0x0003
+pp_data->cap[170]->LinkUsagePage = 0xFF01
+pp_data->cap[170]->LinkUsage = 0x0080
+pp_data->cap[170]->IsMultipleItemsForArray = 0
+pp_data->cap[170]->IsButtonCap = 0
+pp_data->cap[170]->IsPadding = 0
+pp_data->cap[170]->IsAbsolute = 1
+pp_data->cap[170]->IsRange = 0
+pp_data->cap[170]->IsAlias = 0
+pp_data->cap[170]->IsStringRange = 0
+pp_data->cap[170]->IsDesignatorRange = 0
+pp_data->cap[170]->Reserved1 = 0x000000
+pp_data->cap[170]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[170]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[170]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[170]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[170]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[170]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[170]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[170]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[170]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[170]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[170]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[170]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[170]->NotRange.Usage = 0x0081
+pp_data->cap[170]->NotRange.Reserved1 = 0x0081
+pp_data->cap[170]->NotRange.StringIndex = 0
+pp_data->cap[170]->NotRange.Reserved2 = 0
+pp_data->cap[170]->NotRange.DesignatorIndex = 0
+pp_data->cap[170]->NotRange.Reserved3 = 0
+pp_data->cap[170]->NotRange.DataIndex = 83
+pp_data->cap[170]->NotRange.Reserved4 = 83
+pp_data->cap[170]->NotButton.HasNull = 0
+pp_data->cap[170]->NotButton.Reserved4 = 0x000000
+pp_data->cap[170]->NotButton.LogicalMin = 0
+pp_data->cap[170]->NotButton.LogicalMax = 127
+pp_data->cap[170]->NotButton.PhysicalMin = 0
+pp_data->cap[170]->NotButton.PhysicalMax = 0
+pp_data->cap[170]->Units = 0
+pp_data->cap[170]->UnitsExp = 0
+
+pp_data->cap[171]->UsagePage = 0xFF01
+pp_data->cap[171]->ReportID = 0x80
+pp_data->cap[171]->BitPosition = 0
+pp_data->cap[171]->BitSize = 8
+pp_data->cap[171]->ReportCount = 1
+pp_data->cap[171]->BytePosition = 0x000A
+pp_data->cap[171]->BitCount = 8
+pp_data->cap[171]->BitField = 0x02
+pp_data->cap[171]->NextBytePosition = 0x000B
+pp_data->cap[171]->LinkCollection = 0x0003
+pp_data->cap[171]->LinkUsagePage = 0xFF01
+pp_data->cap[171]->LinkUsage = 0x0080
+pp_data->cap[171]->IsMultipleItemsForArray = 0
+pp_data->cap[171]->IsButtonCap = 0
+pp_data->cap[171]->IsPadding = 0
+pp_data->cap[171]->IsAbsolute = 1
+pp_data->cap[171]->IsRange = 0
+pp_data->cap[171]->IsAlias = 0
+pp_data->cap[171]->IsStringRange = 0
+pp_data->cap[171]->IsDesignatorRange = 0
+pp_data->cap[171]->Reserved1 = 0x000000
+pp_data->cap[171]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[171]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[171]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[171]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[171]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[171]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[171]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[171]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[171]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[171]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[171]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[171]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[171]->NotRange.Usage = 0x0081
+pp_data->cap[171]->NotRange.Reserved1 = 0x0081
+pp_data->cap[171]->NotRange.StringIndex = 0
+pp_data->cap[171]->NotRange.Reserved2 = 0
+pp_data->cap[171]->NotRange.DesignatorIndex = 0
+pp_data->cap[171]->NotRange.Reserved3 = 0
+pp_data->cap[171]->NotRange.DataIndex = 84
+pp_data->cap[171]->NotRange.Reserved4 = 84
+pp_data->cap[171]->NotButton.HasNull = 0
+pp_data->cap[171]->NotButton.Reserved4 = 0x000000
+pp_data->cap[171]->NotButton.LogicalMin = 0
+pp_data->cap[171]->NotButton.LogicalMax = 127
+pp_data->cap[171]->NotButton.PhysicalMin = 0
+pp_data->cap[171]->NotButton.PhysicalMax = 0
+pp_data->cap[171]->Units = 0
+pp_data->cap[171]->UnitsExp = 0
+
+pp_data->cap[172]->UsagePage = 0xFF01
+pp_data->cap[172]->ReportID = 0x80
+pp_data->cap[172]->BitPosition = 0
+pp_data->cap[172]->BitSize = 8
+pp_data->cap[172]->ReportCount = 1
+pp_data->cap[172]->BytePosition = 0x0009
+pp_data->cap[172]->BitCount = 8
+pp_data->cap[172]->BitField = 0x02
+pp_data->cap[172]->NextBytePosition = 0x000A
+pp_data->cap[172]->LinkCollection = 0x0003
+pp_data->cap[172]->LinkUsagePage = 0xFF01
+pp_data->cap[172]->LinkUsage = 0x0080
+pp_data->cap[172]->IsMultipleItemsForArray = 0
+pp_data->cap[172]->IsButtonCap = 0
+pp_data->cap[172]->IsPadding = 0
+pp_data->cap[172]->IsAbsolute = 1
+pp_data->cap[172]->IsRange = 0
+pp_data->cap[172]->IsAlias = 0
+pp_data->cap[172]->IsStringRange = 0
+pp_data->cap[172]->IsDesignatorRange = 0
+pp_data->cap[172]->Reserved1 = 0x000000
+pp_data->cap[172]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[172]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[172]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[172]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[172]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[172]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[172]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[172]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[172]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[172]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[172]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[172]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[172]->NotRange.Usage = 0x0081
+pp_data->cap[172]->NotRange.Reserved1 = 0x0081
+pp_data->cap[172]->NotRange.StringIndex = 0
+pp_data->cap[172]->NotRange.Reserved2 = 0
+pp_data->cap[172]->NotRange.DesignatorIndex = 0
+pp_data->cap[172]->NotRange.Reserved3 = 0
+pp_data->cap[172]->NotRange.DataIndex = 85
+pp_data->cap[172]->NotRange.Reserved4 = 85
+pp_data->cap[172]->NotButton.HasNull = 0
+pp_data->cap[172]->NotButton.Reserved4 = 0x000000
+pp_data->cap[172]->NotButton.LogicalMin = 0
+pp_data->cap[172]->NotButton.LogicalMax = 127
+pp_data->cap[172]->NotButton.PhysicalMin = 0
+pp_data->cap[172]->NotButton.PhysicalMax = 0
+pp_data->cap[172]->Units = 0
+pp_data->cap[172]->UnitsExp = 0
+
+pp_data->cap[173]->UsagePage = 0xFF01
+pp_data->cap[173]->ReportID = 0x80
+pp_data->cap[173]->BitPosition = 0
+pp_data->cap[173]->BitSize = 8
+pp_data->cap[173]->ReportCount = 1
+pp_data->cap[173]->BytePosition = 0x0008
+pp_data->cap[173]->BitCount = 8
+pp_data->cap[173]->BitField = 0x02
+pp_data->cap[173]->NextBytePosition = 0x0009
+pp_data->cap[173]->LinkCollection = 0x0003
+pp_data->cap[173]->LinkUsagePage = 0xFF01
+pp_data->cap[173]->LinkUsage = 0x0080
+pp_data->cap[173]->IsMultipleItemsForArray = 0
+pp_data->cap[173]->IsButtonCap = 0
+pp_data->cap[173]->IsPadding = 0
+pp_data->cap[173]->IsAbsolute = 1
+pp_data->cap[173]->IsRange = 0
+pp_data->cap[173]->IsAlias = 0
+pp_data->cap[173]->IsStringRange = 0
+pp_data->cap[173]->IsDesignatorRange = 0
+pp_data->cap[173]->Reserved1 = 0x000000
+pp_data->cap[173]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[173]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[173]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[173]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[173]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[173]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[173]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[173]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[173]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[173]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[173]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[173]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[173]->NotRange.Usage = 0x0081
+pp_data->cap[173]->NotRange.Reserved1 = 0x0081
+pp_data->cap[173]->NotRange.StringIndex = 0
+pp_data->cap[173]->NotRange.Reserved2 = 0
+pp_data->cap[173]->NotRange.DesignatorIndex = 0
+pp_data->cap[173]->NotRange.Reserved3 = 0
+pp_data->cap[173]->NotRange.DataIndex = 86
+pp_data->cap[173]->NotRange.Reserved4 = 86
+pp_data->cap[173]->NotButton.HasNull = 0
+pp_data->cap[173]->NotButton.Reserved4 = 0x000000
+pp_data->cap[173]->NotButton.LogicalMin = 0
+pp_data->cap[173]->NotButton.LogicalMax = 127
+pp_data->cap[173]->NotButton.PhysicalMin = 0
+pp_data->cap[173]->NotButton.PhysicalMax = 0
+pp_data->cap[173]->Units = 0
+pp_data->cap[173]->UnitsExp = 0
+
+pp_data->cap[174]->UsagePage = 0xFF01
+pp_data->cap[174]->ReportID = 0x80
+pp_data->cap[174]->BitPosition = 0
+pp_data->cap[174]->BitSize = 8
+pp_data->cap[174]->ReportCount = 1
+pp_data->cap[174]->BytePosition = 0x0007
+pp_data->cap[174]->BitCount = 8
+pp_data->cap[174]->BitField = 0x02
+pp_data->cap[174]->NextBytePosition = 0x0008
+pp_data->cap[174]->LinkCollection = 0x0003
+pp_data->cap[174]->LinkUsagePage = 0xFF01
+pp_data->cap[174]->LinkUsage = 0x0080
+pp_data->cap[174]->IsMultipleItemsForArray = 0
+pp_data->cap[174]->IsButtonCap = 0
+pp_data->cap[174]->IsPadding = 0
+pp_data->cap[174]->IsAbsolute = 1
+pp_data->cap[174]->IsRange = 0
+pp_data->cap[174]->IsAlias = 0
+pp_data->cap[174]->IsStringRange = 0
+pp_data->cap[174]->IsDesignatorRange = 0
+pp_data->cap[174]->Reserved1 = 0x000000
+pp_data->cap[174]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[174]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[174]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[174]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[174]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[174]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[174]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[174]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[174]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[174]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[174]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[174]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[174]->NotRange.Usage = 0x0081
+pp_data->cap[174]->NotRange.Reserved1 = 0x0081
+pp_data->cap[174]->NotRange.StringIndex = 0
+pp_data->cap[174]->NotRange.Reserved2 = 0
+pp_data->cap[174]->NotRange.DesignatorIndex = 0
+pp_data->cap[174]->NotRange.Reserved3 = 0
+pp_data->cap[174]->NotRange.DataIndex = 87
+pp_data->cap[174]->NotRange.Reserved4 = 87
+pp_data->cap[174]->NotButton.HasNull = 0
+pp_data->cap[174]->NotButton.Reserved4 = 0x000000
+pp_data->cap[174]->NotButton.LogicalMin = 0
+pp_data->cap[174]->NotButton.LogicalMax = 127
+pp_data->cap[174]->NotButton.PhysicalMin = 0
+pp_data->cap[174]->NotButton.PhysicalMax = 0
+pp_data->cap[174]->Units = 0
+pp_data->cap[174]->UnitsExp = 0
+
+pp_data->cap[175]->UsagePage = 0xFF01
+pp_data->cap[175]->ReportID = 0x80
+pp_data->cap[175]->BitPosition = 0
+pp_data->cap[175]->BitSize = 8
+pp_data->cap[175]->ReportCount = 1
+pp_data->cap[175]->BytePosition = 0x0006
+pp_data->cap[175]->BitCount = 8
+pp_data->cap[175]->BitField = 0x02
+pp_data->cap[175]->NextBytePosition = 0x0007
+pp_data->cap[175]->LinkCollection = 0x0003
+pp_data->cap[175]->LinkUsagePage = 0xFF01
+pp_data->cap[175]->LinkUsage = 0x0080
+pp_data->cap[175]->IsMultipleItemsForArray = 0
+pp_data->cap[175]->IsButtonCap = 0
+pp_data->cap[175]->IsPadding = 0
+pp_data->cap[175]->IsAbsolute = 1
+pp_data->cap[175]->IsRange = 0
+pp_data->cap[175]->IsAlias = 0
+pp_data->cap[175]->IsStringRange = 0
+pp_data->cap[175]->IsDesignatorRange = 0
+pp_data->cap[175]->Reserved1 = 0x000000
+pp_data->cap[175]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[175]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[175]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[175]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[175]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[175]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[175]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[175]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[175]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[175]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[175]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[175]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[175]->NotRange.Usage = 0x0081
+pp_data->cap[175]->NotRange.Reserved1 = 0x0081
+pp_data->cap[175]->NotRange.StringIndex = 0
+pp_data->cap[175]->NotRange.Reserved2 = 0
+pp_data->cap[175]->NotRange.DesignatorIndex = 0
+pp_data->cap[175]->NotRange.Reserved3 = 0
+pp_data->cap[175]->NotRange.DataIndex = 88
+pp_data->cap[175]->NotRange.Reserved4 = 88
+pp_data->cap[175]->NotButton.HasNull = 0
+pp_data->cap[175]->NotButton.Reserved4 = 0x000000
+pp_data->cap[175]->NotButton.LogicalMin = 0
+pp_data->cap[175]->NotButton.LogicalMax = 127
+pp_data->cap[175]->NotButton.PhysicalMin = 0
+pp_data->cap[175]->NotButton.PhysicalMax = 0
+pp_data->cap[175]->Units = 0
+pp_data->cap[175]->UnitsExp = 0
+
+pp_data->cap[176]->UsagePage = 0xFF01
+pp_data->cap[176]->ReportID = 0x80
+pp_data->cap[176]->BitPosition = 0
+pp_data->cap[176]->BitSize = 8
+pp_data->cap[176]->ReportCount = 1
+pp_data->cap[176]->BytePosition = 0x0005
+pp_data->cap[176]->BitCount = 8
+pp_data->cap[176]->BitField = 0x02
+pp_data->cap[176]->NextBytePosition = 0x0006
+pp_data->cap[176]->LinkCollection = 0x0003
+pp_data->cap[176]->LinkUsagePage = 0xFF01
+pp_data->cap[176]->LinkUsage = 0x0080
+pp_data->cap[176]->IsMultipleItemsForArray = 0
+pp_data->cap[176]->IsButtonCap = 0
+pp_data->cap[176]->IsPadding = 0
+pp_data->cap[176]->IsAbsolute = 1
+pp_data->cap[176]->IsRange = 0
+pp_data->cap[176]->IsAlias = 0
+pp_data->cap[176]->IsStringRange = 0
+pp_data->cap[176]->IsDesignatorRange = 0
+pp_data->cap[176]->Reserved1 = 0x000000
+pp_data->cap[176]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[176]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[176]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[176]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[176]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[176]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[176]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[176]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[176]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[176]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[176]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[176]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[176]->NotRange.Usage = 0x0081
+pp_data->cap[176]->NotRange.Reserved1 = 0x0081
+pp_data->cap[176]->NotRange.StringIndex = 0
+pp_data->cap[176]->NotRange.Reserved2 = 0
+pp_data->cap[176]->NotRange.DesignatorIndex = 0
+pp_data->cap[176]->NotRange.Reserved3 = 0
+pp_data->cap[176]->NotRange.DataIndex = 89
+pp_data->cap[176]->NotRange.Reserved4 = 89
+pp_data->cap[176]->NotButton.HasNull = 0
+pp_data->cap[176]->NotButton.Reserved4 = 0x000000
+pp_data->cap[176]->NotButton.LogicalMin = 0
+pp_data->cap[176]->NotButton.LogicalMax = 127
+pp_data->cap[176]->NotButton.PhysicalMin = 0
+pp_data->cap[176]->NotButton.PhysicalMax = 0
+pp_data->cap[176]->Units = 0
+pp_data->cap[176]->UnitsExp = 0
+
+pp_data->cap[177]->UsagePage = 0xFF01
+pp_data->cap[177]->ReportID = 0x80
+pp_data->cap[177]->BitPosition = 0
+pp_data->cap[177]->BitSize = 8
+pp_data->cap[177]->ReportCount = 1
+pp_data->cap[177]->BytePosition = 0x0004
+pp_data->cap[177]->BitCount = 8
+pp_data->cap[177]->BitField = 0x02
+pp_data->cap[177]->NextBytePosition = 0x0005
+pp_data->cap[177]->LinkCollection = 0x0003
+pp_data->cap[177]->LinkUsagePage = 0xFF01
+pp_data->cap[177]->LinkUsage = 0x0080
+pp_data->cap[177]->IsMultipleItemsForArray = 0
+pp_data->cap[177]->IsButtonCap = 0
+pp_data->cap[177]->IsPadding = 0
+pp_data->cap[177]->IsAbsolute = 1
+pp_data->cap[177]->IsRange = 0
+pp_data->cap[177]->IsAlias = 0
+pp_data->cap[177]->IsStringRange = 0
+pp_data->cap[177]->IsDesignatorRange = 0
+pp_data->cap[177]->Reserved1 = 0x000000
+pp_data->cap[177]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[177]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[177]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[177]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[177]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[177]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[177]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[177]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[177]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[177]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[177]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[177]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[177]->NotRange.Usage = 0x0081
+pp_data->cap[177]->NotRange.Reserved1 = 0x0081
+pp_data->cap[177]->NotRange.StringIndex = 0
+pp_data->cap[177]->NotRange.Reserved2 = 0
+pp_data->cap[177]->NotRange.DesignatorIndex = 0
+pp_data->cap[177]->NotRange.Reserved3 = 0
+pp_data->cap[177]->NotRange.DataIndex = 90
+pp_data->cap[177]->NotRange.Reserved4 = 90
+pp_data->cap[177]->NotButton.HasNull = 0
+pp_data->cap[177]->NotButton.Reserved4 = 0x000000
+pp_data->cap[177]->NotButton.LogicalMin = 0
+pp_data->cap[177]->NotButton.LogicalMax = 127
+pp_data->cap[177]->NotButton.PhysicalMin = 0
+pp_data->cap[177]->NotButton.PhysicalMax = 0
+pp_data->cap[177]->Units = 0
+pp_data->cap[177]->UnitsExp = 0
+
+pp_data->cap[178]->UsagePage = 0xFF01
+pp_data->cap[178]->ReportID = 0x80
+pp_data->cap[178]->BitPosition = 0
+pp_data->cap[178]->BitSize = 8
+pp_data->cap[178]->ReportCount = 1
+pp_data->cap[178]->BytePosition = 0x0003
+pp_data->cap[178]->BitCount = 8
+pp_data->cap[178]->BitField = 0x02
+pp_data->cap[178]->NextBytePosition = 0x0004
+pp_data->cap[178]->LinkCollection = 0x0003
+pp_data->cap[178]->LinkUsagePage = 0xFF01
+pp_data->cap[178]->LinkUsage = 0x0080
+pp_data->cap[178]->IsMultipleItemsForArray = 0
+pp_data->cap[178]->IsButtonCap = 0
+pp_data->cap[178]->IsPadding = 0
+pp_data->cap[178]->IsAbsolute = 1
+pp_data->cap[178]->IsRange = 0
+pp_data->cap[178]->IsAlias = 0
+pp_data->cap[178]->IsStringRange = 0
+pp_data->cap[178]->IsDesignatorRange = 0
+pp_data->cap[178]->Reserved1 = 0x000000
+pp_data->cap[178]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[178]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[178]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[178]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[178]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[178]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[178]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[178]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[178]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[178]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[178]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[178]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[178]->NotRange.Usage = 0x0081
+pp_data->cap[178]->NotRange.Reserved1 = 0x0081
+pp_data->cap[178]->NotRange.StringIndex = 0
+pp_data->cap[178]->NotRange.Reserved2 = 0
+pp_data->cap[178]->NotRange.DesignatorIndex = 0
+pp_data->cap[178]->NotRange.Reserved3 = 0
+pp_data->cap[178]->NotRange.DataIndex = 91
+pp_data->cap[178]->NotRange.Reserved4 = 91
+pp_data->cap[178]->NotButton.HasNull = 0
+pp_data->cap[178]->NotButton.Reserved4 = 0x000000
+pp_data->cap[178]->NotButton.LogicalMin = 0
+pp_data->cap[178]->NotButton.LogicalMax = 127
+pp_data->cap[178]->NotButton.PhysicalMin = 0
+pp_data->cap[178]->NotButton.PhysicalMax = 0
+pp_data->cap[178]->Units = 0
+pp_data->cap[178]->UnitsExp = 0
+
+pp_data->cap[179]->UsagePage = 0xFF01
+pp_data->cap[179]->ReportID = 0x80
+pp_data->cap[179]->BitPosition = 0
+pp_data->cap[179]->BitSize = 8
+pp_data->cap[179]->ReportCount = 1
+pp_data->cap[179]->BytePosition = 0x0002
+pp_data->cap[179]->BitCount = 8
+pp_data->cap[179]->BitField = 0x02
+pp_data->cap[179]->NextBytePosition = 0x0003
+pp_data->cap[179]->LinkCollection = 0x0003
+pp_data->cap[179]->LinkUsagePage = 0xFF01
+pp_data->cap[179]->LinkUsage = 0x0080
+pp_data->cap[179]->IsMultipleItemsForArray = 0
+pp_data->cap[179]->IsButtonCap = 0
+pp_data->cap[179]->IsPadding = 0
+pp_data->cap[179]->IsAbsolute = 1
+pp_data->cap[179]->IsRange = 0
+pp_data->cap[179]->IsAlias = 0
+pp_data->cap[179]->IsStringRange = 0
+pp_data->cap[179]->IsDesignatorRange = 0
+pp_data->cap[179]->Reserved1 = 0x000000
+pp_data->cap[179]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[179]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[179]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[179]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[179]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[179]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[179]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[179]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[179]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[179]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[179]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[179]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[179]->NotRange.Usage = 0x0081
+pp_data->cap[179]->NotRange.Reserved1 = 0x0081
+pp_data->cap[179]->NotRange.StringIndex = 0
+pp_data->cap[179]->NotRange.Reserved2 = 0
+pp_data->cap[179]->NotRange.DesignatorIndex = 0
+pp_data->cap[179]->NotRange.Reserved3 = 0
+pp_data->cap[179]->NotRange.DataIndex = 92
+pp_data->cap[179]->NotRange.Reserved4 = 92
+pp_data->cap[179]->NotButton.HasNull = 0
+pp_data->cap[179]->NotButton.Reserved4 = 0x000000
+pp_data->cap[179]->NotButton.LogicalMin = 0
+pp_data->cap[179]->NotButton.LogicalMax = 127
+pp_data->cap[179]->NotButton.PhysicalMin = 0
+pp_data->cap[179]->NotButton.PhysicalMax = 0
+pp_data->cap[179]->Units = 0
+pp_data->cap[179]->UnitsExp = 0
+
+pp_data->cap[180]->UsagePage = 0xFF01
+pp_data->cap[180]->ReportID = 0x80
+pp_data->cap[180]->BitPosition = 0
+pp_data->cap[180]->BitSize = 8
+pp_data->cap[180]->ReportCount = 1
+pp_data->cap[180]->BytePosition = 0x0001
+pp_data->cap[180]->BitCount = 8
+pp_data->cap[180]->BitField = 0x02
+pp_data->cap[180]->NextBytePosition = 0x0002
+pp_data->cap[180]->LinkCollection = 0x0003
+pp_data->cap[180]->LinkUsagePage = 0xFF01
+pp_data->cap[180]->LinkUsage = 0x0080
+pp_data->cap[180]->IsMultipleItemsForArray = 0
+pp_data->cap[180]->IsButtonCap = 0
+pp_data->cap[180]->IsPadding = 0
+pp_data->cap[180]->IsAbsolute = 1
+pp_data->cap[180]->IsRange = 0
+pp_data->cap[180]->IsAlias = 0
+pp_data->cap[180]->IsStringRange = 0
+pp_data->cap[180]->IsDesignatorRange = 0
+pp_data->cap[180]->Reserved1 = 0x000000
+pp_data->cap[180]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[180]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[180]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[180]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[180]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[180]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[180]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[180]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[180]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[180]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[180]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[180]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[180]->NotRange.Usage = 0x0081
+pp_data->cap[180]->NotRange.Reserved1 = 0x0081
+pp_data->cap[180]->NotRange.StringIndex = 0
+pp_data->cap[180]->NotRange.Reserved2 = 0
+pp_data->cap[180]->NotRange.DesignatorIndex = 0
+pp_data->cap[180]->NotRange.Reserved3 = 0
+pp_data->cap[180]->NotRange.DataIndex = 93
+pp_data->cap[180]->NotRange.Reserved4 = 93
+pp_data->cap[180]->NotButton.HasNull = 0
+pp_data->cap[180]->NotButton.Reserved4 = 0x000000
+pp_data->cap[180]->NotButton.LogicalMin = 0
+pp_data->cap[180]->NotButton.LogicalMax = 127
+pp_data->cap[180]->NotButton.PhysicalMin = 0
+pp_data->cap[180]->NotButton.PhysicalMax = 0
+pp_data->cap[180]->Units = 0
+pp_data->cap[180]->UnitsExp = 0
+
+pp_data->cap[181]->UsagePage = 0xFF01
+pp_data->cap[181]->ReportID = 0x81
+pp_data->cap[181]->BitPosition = 0
+pp_data->cap[181]->BitSize = 8
+pp_data->cap[181]->ReportCount = 1
+pp_data->cap[181]->BytePosition = 0x0028
+pp_data->cap[181]->BitCount = 8
+pp_data->cap[181]->BitField = 0x02
+pp_data->cap[181]->NextBytePosition = 0x0029
+pp_data->cap[181]->LinkCollection = 0x0004
+pp_data->cap[181]->LinkUsagePage = 0xFF01
+pp_data->cap[181]->LinkUsage = 0x0080
+pp_data->cap[181]->IsMultipleItemsForArray = 0
+pp_data->cap[181]->IsButtonCap = 0
+pp_data->cap[181]->IsPadding = 0
+pp_data->cap[181]->IsAbsolute = 1
+pp_data->cap[181]->IsRange = 0
+pp_data->cap[181]->IsAlias = 0
+pp_data->cap[181]->IsStringRange = 0
+pp_data->cap[181]->IsDesignatorRange = 0
+pp_data->cap[181]->Reserved1 = 0x000000
+pp_data->cap[181]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[181]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[181]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[181]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[181]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[181]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[181]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[181]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[181]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[181]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[181]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[181]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[181]->NotRange.Usage = 0x0081
+pp_data->cap[181]->NotRange.Reserved1 = 0x0081
+pp_data->cap[181]->NotRange.StringIndex = 0
+pp_data->cap[181]->NotRange.Reserved2 = 0
+pp_data->cap[181]->NotRange.DesignatorIndex = 0
+pp_data->cap[181]->NotRange.Reserved3 = 0
+pp_data->cap[181]->NotRange.DataIndex = 94
+pp_data->cap[181]->NotRange.Reserved4 = 94
+pp_data->cap[181]->NotButton.HasNull = 0
+pp_data->cap[181]->NotButton.Reserved4 = 0x000000
+pp_data->cap[181]->NotButton.LogicalMin = 0
+pp_data->cap[181]->NotButton.LogicalMax = 127
+pp_data->cap[181]->NotButton.PhysicalMin = 0
+pp_data->cap[181]->NotButton.PhysicalMax = 0
+pp_data->cap[181]->Units = 0
+pp_data->cap[181]->UnitsExp = 0
+
+pp_data->cap[182]->UsagePage = 0xFF01
+pp_data->cap[182]->ReportID = 0x81
+pp_data->cap[182]->BitPosition = 0
+pp_data->cap[182]->BitSize = 8
+pp_data->cap[182]->ReportCount = 1
+pp_data->cap[182]->BytePosition = 0x0027
+pp_data->cap[182]->BitCount = 8
+pp_data->cap[182]->BitField = 0x02
+pp_data->cap[182]->NextBytePosition = 0x0028
+pp_data->cap[182]->LinkCollection = 0x0004
+pp_data->cap[182]->LinkUsagePage = 0xFF01
+pp_data->cap[182]->LinkUsage = 0x0080
+pp_data->cap[182]->IsMultipleItemsForArray = 0
+pp_data->cap[182]->IsButtonCap = 0
+pp_data->cap[182]->IsPadding = 0
+pp_data->cap[182]->IsAbsolute = 1
+pp_data->cap[182]->IsRange = 0
+pp_data->cap[182]->IsAlias = 0
+pp_data->cap[182]->IsStringRange = 0
+pp_data->cap[182]->IsDesignatorRange = 0
+pp_data->cap[182]->Reserved1 = 0x000000
+pp_data->cap[182]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[182]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[182]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[182]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[182]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[182]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[182]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[182]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[182]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[182]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[182]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[182]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[182]->NotRange.Usage = 0x0081
+pp_data->cap[182]->NotRange.Reserved1 = 0x0081
+pp_data->cap[182]->NotRange.StringIndex = 0
+pp_data->cap[182]->NotRange.Reserved2 = 0
+pp_data->cap[182]->NotRange.DesignatorIndex = 0
+pp_data->cap[182]->NotRange.Reserved3 = 0
+pp_data->cap[182]->NotRange.DataIndex = 95
+pp_data->cap[182]->NotRange.Reserved4 = 95
+pp_data->cap[182]->NotButton.HasNull = 0
+pp_data->cap[182]->NotButton.Reserved4 = 0x000000
+pp_data->cap[182]->NotButton.LogicalMin = 0
+pp_data->cap[182]->NotButton.LogicalMax = 127
+pp_data->cap[182]->NotButton.PhysicalMin = 0
+pp_data->cap[182]->NotButton.PhysicalMax = 0
+pp_data->cap[182]->Units = 0
+pp_data->cap[182]->UnitsExp = 0
+
+pp_data->cap[183]->UsagePage = 0xFF01
+pp_data->cap[183]->ReportID = 0x81
+pp_data->cap[183]->BitPosition = 0
+pp_data->cap[183]->BitSize = 8
+pp_data->cap[183]->ReportCount = 1
+pp_data->cap[183]->BytePosition = 0x0026
+pp_data->cap[183]->BitCount = 8
+pp_data->cap[183]->BitField = 0x02
+pp_data->cap[183]->NextBytePosition = 0x0027
+pp_data->cap[183]->LinkCollection = 0x0004
+pp_data->cap[183]->LinkUsagePage = 0xFF01
+pp_data->cap[183]->LinkUsage = 0x0080
+pp_data->cap[183]->IsMultipleItemsForArray = 0
+pp_data->cap[183]->IsButtonCap = 0
+pp_data->cap[183]->IsPadding = 0
+pp_data->cap[183]->IsAbsolute = 1
+pp_data->cap[183]->IsRange = 0
+pp_data->cap[183]->IsAlias = 0
+pp_data->cap[183]->IsStringRange = 0
+pp_data->cap[183]->IsDesignatorRange = 0
+pp_data->cap[183]->Reserved1 = 0x000000
+pp_data->cap[183]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[183]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[183]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[183]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[183]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[183]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[183]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[183]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[183]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[183]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[183]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[183]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[183]->NotRange.Usage = 0x0081
+pp_data->cap[183]->NotRange.Reserved1 = 0x0081
+pp_data->cap[183]->NotRange.StringIndex = 0
+pp_data->cap[183]->NotRange.Reserved2 = 0
+pp_data->cap[183]->NotRange.DesignatorIndex = 0
+pp_data->cap[183]->NotRange.Reserved3 = 0
+pp_data->cap[183]->NotRange.DataIndex = 96
+pp_data->cap[183]->NotRange.Reserved4 = 96
+pp_data->cap[183]->NotButton.HasNull = 0
+pp_data->cap[183]->NotButton.Reserved4 = 0x000000
+pp_data->cap[183]->NotButton.LogicalMin = 0
+pp_data->cap[183]->NotButton.LogicalMax = 127
+pp_data->cap[183]->NotButton.PhysicalMin = 0
+pp_data->cap[183]->NotButton.PhysicalMax = 0
+pp_data->cap[183]->Units = 0
+pp_data->cap[183]->UnitsExp = 0
+
+pp_data->cap[184]->UsagePage = 0xFF01
+pp_data->cap[184]->ReportID = 0x81
+pp_data->cap[184]->BitPosition = 0
+pp_data->cap[184]->BitSize = 8
+pp_data->cap[184]->ReportCount = 1
+pp_data->cap[184]->BytePosition = 0x0025
+pp_data->cap[184]->BitCount = 8
+pp_data->cap[184]->BitField = 0x02
+pp_data->cap[184]->NextBytePosition = 0x0026
+pp_data->cap[184]->LinkCollection = 0x0004
+pp_data->cap[184]->LinkUsagePage = 0xFF01
+pp_data->cap[184]->LinkUsage = 0x0080
+pp_data->cap[184]->IsMultipleItemsForArray = 0
+pp_data->cap[184]->IsButtonCap = 0
+pp_data->cap[184]->IsPadding = 0
+pp_data->cap[184]->IsAbsolute = 1
+pp_data->cap[184]->IsRange = 0
+pp_data->cap[184]->IsAlias = 0
+pp_data->cap[184]->IsStringRange = 0
+pp_data->cap[184]->IsDesignatorRange = 0
+pp_data->cap[184]->Reserved1 = 0x000000
+pp_data->cap[184]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[184]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[184]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[184]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[184]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[184]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[184]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[184]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[184]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[184]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[184]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[184]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[184]->NotRange.Usage = 0x0081
+pp_data->cap[184]->NotRange.Reserved1 = 0x0081
+pp_data->cap[184]->NotRange.StringIndex = 0
+pp_data->cap[184]->NotRange.Reserved2 = 0
+pp_data->cap[184]->NotRange.DesignatorIndex = 0
+pp_data->cap[184]->NotRange.Reserved3 = 0
+pp_data->cap[184]->NotRange.DataIndex = 97
+pp_data->cap[184]->NotRange.Reserved4 = 97
+pp_data->cap[184]->NotButton.HasNull = 0
+pp_data->cap[184]->NotButton.Reserved4 = 0x000000
+pp_data->cap[184]->NotButton.LogicalMin = 0
+pp_data->cap[184]->NotButton.LogicalMax = 127
+pp_data->cap[184]->NotButton.PhysicalMin = 0
+pp_data->cap[184]->NotButton.PhysicalMax = 0
+pp_data->cap[184]->Units = 0
+pp_data->cap[184]->UnitsExp = 0
+
+pp_data->cap[185]->UsagePage = 0xFF01
+pp_data->cap[185]->ReportID = 0x81
+pp_data->cap[185]->BitPosition = 0
+pp_data->cap[185]->BitSize = 8
+pp_data->cap[185]->ReportCount = 1
+pp_data->cap[185]->BytePosition = 0x0024
+pp_data->cap[185]->BitCount = 8
+pp_data->cap[185]->BitField = 0x02
+pp_data->cap[185]->NextBytePosition = 0x0025
+pp_data->cap[185]->LinkCollection = 0x0004
+pp_data->cap[185]->LinkUsagePage = 0xFF01
+pp_data->cap[185]->LinkUsage = 0x0080
+pp_data->cap[185]->IsMultipleItemsForArray = 0
+pp_data->cap[185]->IsButtonCap = 0
+pp_data->cap[185]->IsPadding = 0
+pp_data->cap[185]->IsAbsolute = 1
+pp_data->cap[185]->IsRange = 0
+pp_data->cap[185]->IsAlias = 0
+pp_data->cap[185]->IsStringRange = 0
+pp_data->cap[185]->IsDesignatorRange = 0
+pp_data->cap[185]->Reserved1 = 0x000000
+pp_data->cap[185]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[185]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[185]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[185]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[185]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[185]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[185]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[185]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[185]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[185]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[185]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[185]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[185]->NotRange.Usage = 0x0081
+pp_data->cap[185]->NotRange.Reserved1 = 0x0081
+pp_data->cap[185]->NotRange.StringIndex = 0
+pp_data->cap[185]->NotRange.Reserved2 = 0
+pp_data->cap[185]->NotRange.DesignatorIndex = 0
+pp_data->cap[185]->NotRange.Reserved3 = 0
+pp_data->cap[185]->NotRange.DataIndex = 98
+pp_data->cap[185]->NotRange.Reserved4 = 98
+pp_data->cap[185]->NotButton.HasNull = 0
+pp_data->cap[185]->NotButton.Reserved4 = 0x000000
+pp_data->cap[185]->NotButton.LogicalMin = 0
+pp_data->cap[185]->NotButton.LogicalMax = 127
+pp_data->cap[185]->NotButton.PhysicalMin = 0
+pp_data->cap[185]->NotButton.PhysicalMax = 0
+pp_data->cap[185]->Units = 0
+pp_data->cap[185]->UnitsExp = 0
+
+pp_data->cap[186]->UsagePage = 0xFF01
+pp_data->cap[186]->ReportID = 0x81
+pp_data->cap[186]->BitPosition = 0
+pp_data->cap[186]->BitSize = 8
+pp_data->cap[186]->ReportCount = 1
+pp_data->cap[186]->BytePosition = 0x0023
+pp_data->cap[186]->BitCount = 8
+pp_data->cap[186]->BitField = 0x02
+pp_data->cap[186]->NextBytePosition = 0x0024
+pp_data->cap[186]->LinkCollection = 0x0004
+pp_data->cap[186]->LinkUsagePage = 0xFF01
+pp_data->cap[186]->LinkUsage = 0x0080
+pp_data->cap[186]->IsMultipleItemsForArray = 0
+pp_data->cap[186]->IsButtonCap = 0
+pp_data->cap[186]->IsPadding = 0
+pp_data->cap[186]->IsAbsolute = 1
+pp_data->cap[186]->IsRange = 0
+pp_data->cap[186]->IsAlias = 0
+pp_data->cap[186]->IsStringRange = 0
+pp_data->cap[186]->IsDesignatorRange = 0
+pp_data->cap[186]->Reserved1 = 0x000000
+pp_data->cap[186]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[186]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[186]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[186]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[186]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[186]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[186]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[186]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[186]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[186]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[186]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[186]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[186]->NotRange.Usage = 0x0081
+pp_data->cap[186]->NotRange.Reserved1 = 0x0081
+pp_data->cap[186]->NotRange.StringIndex = 0
+pp_data->cap[186]->NotRange.Reserved2 = 0
+pp_data->cap[186]->NotRange.DesignatorIndex = 0
+pp_data->cap[186]->NotRange.Reserved3 = 0
+pp_data->cap[186]->NotRange.DataIndex = 99
+pp_data->cap[186]->NotRange.Reserved4 = 99
+pp_data->cap[186]->NotButton.HasNull = 0
+pp_data->cap[186]->NotButton.Reserved4 = 0x000000
+pp_data->cap[186]->NotButton.LogicalMin = 0
+pp_data->cap[186]->NotButton.LogicalMax = 127
+pp_data->cap[186]->NotButton.PhysicalMin = 0
+pp_data->cap[186]->NotButton.PhysicalMax = 0
+pp_data->cap[186]->Units = 0
+pp_data->cap[186]->UnitsExp = 0
+
+pp_data->cap[187]->UsagePage = 0xFF01
+pp_data->cap[187]->ReportID = 0x81
+pp_data->cap[187]->BitPosition = 0
+pp_data->cap[187]->BitSize = 8
+pp_data->cap[187]->ReportCount = 1
+pp_data->cap[187]->BytePosition = 0x0022
+pp_data->cap[187]->BitCount = 8
+pp_data->cap[187]->BitField = 0x02
+pp_data->cap[187]->NextBytePosition = 0x0023
+pp_data->cap[187]->LinkCollection = 0x0004
+pp_data->cap[187]->LinkUsagePage = 0xFF01
+pp_data->cap[187]->LinkUsage = 0x0080
+pp_data->cap[187]->IsMultipleItemsForArray = 0
+pp_data->cap[187]->IsButtonCap = 0
+pp_data->cap[187]->IsPadding = 0
+pp_data->cap[187]->IsAbsolute = 1
+pp_data->cap[187]->IsRange = 0
+pp_data->cap[187]->IsAlias = 0
+pp_data->cap[187]->IsStringRange = 0
+pp_data->cap[187]->IsDesignatorRange = 0
+pp_data->cap[187]->Reserved1 = 0x000000
+pp_data->cap[187]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[187]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[187]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[187]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[187]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[187]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[187]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[187]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[187]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[187]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[187]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[187]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[187]->NotRange.Usage = 0x0081
+pp_data->cap[187]->NotRange.Reserved1 = 0x0081
+pp_data->cap[187]->NotRange.StringIndex = 0
+pp_data->cap[187]->NotRange.Reserved2 = 0
+pp_data->cap[187]->NotRange.DesignatorIndex = 0
+pp_data->cap[187]->NotRange.Reserved3 = 0
+pp_data->cap[187]->NotRange.DataIndex = 100
+pp_data->cap[187]->NotRange.Reserved4 = 100
+pp_data->cap[187]->NotButton.HasNull = 0
+pp_data->cap[187]->NotButton.Reserved4 = 0x000000
+pp_data->cap[187]->NotButton.LogicalMin = 0
+pp_data->cap[187]->NotButton.LogicalMax = 127
+pp_data->cap[187]->NotButton.PhysicalMin = 0
+pp_data->cap[187]->NotButton.PhysicalMax = 0
+pp_data->cap[187]->Units = 0
+pp_data->cap[187]->UnitsExp = 0
+
+pp_data->cap[188]->UsagePage = 0xFF01
+pp_data->cap[188]->ReportID = 0x81
+pp_data->cap[188]->BitPosition = 0
+pp_data->cap[188]->BitSize = 8
+pp_data->cap[188]->ReportCount = 1
+pp_data->cap[188]->BytePosition = 0x0021
+pp_data->cap[188]->BitCount = 8
+pp_data->cap[188]->BitField = 0x02
+pp_data->cap[188]->NextBytePosition = 0x0022
+pp_data->cap[188]->LinkCollection = 0x0004
+pp_data->cap[188]->LinkUsagePage = 0xFF01
+pp_data->cap[188]->LinkUsage = 0x0080
+pp_data->cap[188]->IsMultipleItemsForArray = 0
+pp_data->cap[188]->IsButtonCap = 0
+pp_data->cap[188]->IsPadding = 0
+pp_data->cap[188]->IsAbsolute = 1
+pp_data->cap[188]->IsRange = 0
+pp_data->cap[188]->IsAlias = 0
+pp_data->cap[188]->IsStringRange = 0
+pp_data->cap[188]->IsDesignatorRange = 0
+pp_data->cap[188]->Reserved1 = 0x000000
+pp_data->cap[188]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[188]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[188]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[188]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[188]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[188]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[188]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[188]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[188]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[188]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[188]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[188]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[188]->NotRange.Usage = 0x0081
+pp_data->cap[188]->NotRange.Reserved1 = 0x0081
+pp_data->cap[188]->NotRange.StringIndex = 0
+pp_data->cap[188]->NotRange.Reserved2 = 0
+pp_data->cap[188]->NotRange.DesignatorIndex = 0
+pp_data->cap[188]->NotRange.Reserved3 = 0
+pp_data->cap[188]->NotRange.DataIndex = 101
+pp_data->cap[188]->NotRange.Reserved4 = 101
+pp_data->cap[188]->NotButton.HasNull = 0
+pp_data->cap[188]->NotButton.Reserved4 = 0x000000
+pp_data->cap[188]->NotButton.LogicalMin = 0
+pp_data->cap[188]->NotButton.LogicalMax = 127
+pp_data->cap[188]->NotButton.PhysicalMin = 0
+pp_data->cap[188]->NotButton.PhysicalMax = 0
+pp_data->cap[188]->Units = 0
+pp_data->cap[188]->UnitsExp = 0
+
+pp_data->cap[189]->UsagePage = 0xFF01
+pp_data->cap[189]->ReportID = 0x81
+pp_data->cap[189]->BitPosition = 0
+pp_data->cap[189]->BitSize = 8
+pp_data->cap[189]->ReportCount = 1
+pp_data->cap[189]->BytePosition = 0x0020
+pp_data->cap[189]->BitCount = 8
+pp_data->cap[189]->BitField = 0x02
+pp_data->cap[189]->NextBytePosition = 0x0021
+pp_data->cap[189]->LinkCollection = 0x0004
+pp_data->cap[189]->LinkUsagePage = 0xFF01
+pp_data->cap[189]->LinkUsage = 0x0080
+pp_data->cap[189]->IsMultipleItemsForArray = 0
+pp_data->cap[189]->IsButtonCap = 0
+pp_data->cap[189]->IsPadding = 0
+pp_data->cap[189]->IsAbsolute = 1
+pp_data->cap[189]->IsRange = 0
+pp_data->cap[189]->IsAlias = 0
+pp_data->cap[189]->IsStringRange = 0
+pp_data->cap[189]->IsDesignatorRange = 0
+pp_data->cap[189]->Reserved1 = 0x000000
+pp_data->cap[189]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[189]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[189]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[189]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[189]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[189]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[189]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[189]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[189]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[189]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[189]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[189]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[189]->NotRange.Usage = 0x0081
+pp_data->cap[189]->NotRange.Reserved1 = 0x0081
+pp_data->cap[189]->NotRange.StringIndex = 0
+pp_data->cap[189]->NotRange.Reserved2 = 0
+pp_data->cap[189]->NotRange.DesignatorIndex = 0
+pp_data->cap[189]->NotRange.Reserved3 = 0
+pp_data->cap[189]->NotRange.DataIndex = 102
+pp_data->cap[189]->NotRange.Reserved4 = 102
+pp_data->cap[189]->NotButton.HasNull = 0
+pp_data->cap[189]->NotButton.Reserved4 = 0x000000
+pp_data->cap[189]->NotButton.LogicalMin = 0
+pp_data->cap[189]->NotButton.LogicalMax = 127
+pp_data->cap[189]->NotButton.PhysicalMin = 0
+pp_data->cap[189]->NotButton.PhysicalMax = 0
+pp_data->cap[189]->Units = 0
+pp_data->cap[189]->UnitsExp = 0
+
+pp_data->cap[190]->UsagePage = 0xFF01
+pp_data->cap[190]->ReportID = 0x81
+pp_data->cap[190]->BitPosition = 0
+pp_data->cap[190]->BitSize = 8
+pp_data->cap[190]->ReportCount = 1
+pp_data->cap[190]->BytePosition = 0x001F
+pp_data->cap[190]->BitCount = 8
+pp_data->cap[190]->BitField = 0x02
+pp_data->cap[190]->NextBytePosition = 0x0020
+pp_data->cap[190]->LinkCollection = 0x0004
+pp_data->cap[190]->LinkUsagePage = 0xFF01
+pp_data->cap[190]->LinkUsage = 0x0080
+pp_data->cap[190]->IsMultipleItemsForArray = 0
+pp_data->cap[190]->IsButtonCap = 0
+pp_data->cap[190]->IsPadding = 0
+pp_data->cap[190]->IsAbsolute = 1
+pp_data->cap[190]->IsRange = 0
+pp_data->cap[190]->IsAlias = 0
+pp_data->cap[190]->IsStringRange = 0
+pp_data->cap[190]->IsDesignatorRange = 0
+pp_data->cap[190]->Reserved1 = 0x000000
+pp_data->cap[190]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[190]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[190]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[190]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[190]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[190]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[190]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[190]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[190]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[190]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[190]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[190]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[190]->NotRange.Usage = 0x0081
+pp_data->cap[190]->NotRange.Reserved1 = 0x0081
+pp_data->cap[190]->NotRange.StringIndex = 0
+pp_data->cap[190]->NotRange.Reserved2 = 0
+pp_data->cap[190]->NotRange.DesignatorIndex = 0
+pp_data->cap[190]->NotRange.Reserved3 = 0
+pp_data->cap[190]->NotRange.DataIndex = 103
+pp_data->cap[190]->NotRange.Reserved4 = 103
+pp_data->cap[190]->NotButton.HasNull = 0
+pp_data->cap[190]->NotButton.Reserved4 = 0x000000
+pp_data->cap[190]->NotButton.LogicalMin = 0
+pp_data->cap[190]->NotButton.LogicalMax = 127
+pp_data->cap[190]->NotButton.PhysicalMin = 0
+pp_data->cap[190]->NotButton.PhysicalMax = 0
+pp_data->cap[190]->Units = 0
+pp_data->cap[190]->UnitsExp = 0
+
+pp_data->cap[191]->UsagePage = 0xFF01
+pp_data->cap[191]->ReportID = 0x81
+pp_data->cap[191]->BitPosition = 0
+pp_data->cap[191]->BitSize = 8
+pp_data->cap[191]->ReportCount = 1
+pp_data->cap[191]->BytePosition = 0x001E
+pp_data->cap[191]->BitCount = 8
+pp_data->cap[191]->BitField = 0x02
+pp_data->cap[191]->NextBytePosition = 0x001F
+pp_data->cap[191]->LinkCollection = 0x0004
+pp_data->cap[191]->LinkUsagePage = 0xFF01
+pp_data->cap[191]->LinkUsage = 0x0080
+pp_data->cap[191]->IsMultipleItemsForArray = 0
+pp_data->cap[191]->IsButtonCap = 0
+pp_data->cap[191]->IsPadding = 0
+pp_data->cap[191]->IsAbsolute = 1
+pp_data->cap[191]->IsRange = 0
+pp_data->cap[191]->IsAlias = 0
+pp_data->cap[191]->IsStringRange = 0
+pp_data->cap[191]->IsDesignatorRange = 0
+pp_data->cap[191]->Reserved1 = 0x000000
+pp_data->cap[191]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[191]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[191]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[191]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[191]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[191]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[191]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[191]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[191]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[191]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[191]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[191]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[191]->NotRange.Usage = 0x0081
+pp_data->cap[191]->NotRange.Reserved1 = 0x0081
+pp_data->cap[191]->NotRange.StringIndex = 0
+pp_data->cap[191]->NotRange.Reserved2 = 0
+pp_data->cap[191]->NotRange.DesignatorIndex = 0
+pp_data->cap[191]->NotRange.Reserved3 = 0
+pp_data->cap[191]->NotRange.DataIndex = 104
+pp_data->cap[191]->NotRange.Reserved4 = 104
+pp_data->cap[191]->NotButton.HasNull = 0
+pp_data->cap[191]->NotButton.Reserved4 = 0x000000
+pp_data->cap[191]->NotButton.LogicalMin = 0
+pp_data->cap[191]->NotButton.LogicalMax = 127
+pp_data->cap[191]->NotButton.PhysicalMin = 0
+pp_data->cap[191]->NotButton.PhysicalMax = 0
+pp_data->cap[191]->Units = 0
+pp_data->cap[191]->UnitsExp = 0
+
+pp_data->cap[192]->UsagePage = 0xFF01
+pp_data->cap[192]->ReportID = 0x81
+pp_data->cap[192]->BitPosition = 0
+pp_data->cap[192]->BitSize = 8
+pp_data->cap[192]->ReportCount = 1
+pp_data->cap[192]->BytePosition = 0x001D
+pp_data->cap[192]->BitCount = 8
+pp_data->cap[192]->BitField = 0x02
+pp_data->cap[192]->NextBytePosition = 0x001E
+pp_data->cap[192]->LinkCollection = 0x0004
+pp_data->cap[192]->LinkUsagePage = 0xFF01
+pp_data->cap[192]->LinkUsage = 0x0080
+pp_data->cap[192]->IsMultipleItemsForArray = 0
+pp_data->cap[192]->IsButtonCap = 0
+pp_data->cap[192]->IsPadding = 0
+pp_data->cap[192]->IsAbsolute = 1
+pp_data->cap[192]->IsRange = 0
+pp_data->cap[192]->IsAlias = 0
+pp_data->cap[192]->IsStringRange = 0
+pp_data->cap[192]->IsDesignatorRange = 0
+pp_data->cap[192]->Reserved1 = 0x000000
+pp_data->cap[192]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[192]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[192]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[192]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[192]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[192]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[192]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[192]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[192]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[192]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[192]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[192]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[192]->NotRange.Usage = 0x0081
+pp_data->cap[192]->NotRange.Reserved1 = 0x0081
+pp_data->cap[192]->NotRange.StringIndex = 0
+pp_data->cap[192]->NotRange.Reserved2 = 0
+pp_data->cap[192]->NotRange.DesignatorIndex = 0
+pp_data->cap[192]->NotRange.Reserved3 = 0
+pp_data->cap[192]->NotRange.DataIndex = 105
+pp_data->cap[192]->NotRange.Reserved4 = 105
+pp_data->cap[192]->NotButton.HasNull = 0
+pp_data->cap[192]->NotButton.Reserved4 = 0x000000
+pp_data->cap[192]->NotButton.LogicalMin = 0
+pp_data->cap[192]->NotButton.LogicalMax = 127
+pp_data->cap[192]->NotButton.PhysicalMin = 0
+pp_data->cap[192]->NotButton.PhysicalMax = 0
+pp_data->cap[192]->Units = 0
+pp_data->cap[192]->UnitsExp = 0
+
+pp_data->cap[193]->UsagePage = 0xFF01
+pp_data->cap[193]->ReportID = 0x81
+pp_data->cap[193]->BitPosition = 0
+pp_data->cap[193]->BitSize = 8
+pp_data->cap[193]->ReportCount = 1
+pp_data->cap[193]->BytePosition = 0x001C
+pp_data->cap[193]->BitCount = 8
+pp_data->cap[193]->BitField = 0x02
+pp_data->cap[193]->NextBytePosition = 0x001D
+pp_data->cap[193]->LinkCollection = 0x0004
+pp_data->cap[193]->LinkUsagePage = 0xFF01
+pp_data->cap[193]->LinkUsage = 0x0080
+pp_data->cap[193]->IsMultipleItemsForArray = 0
+pp_data->cap[193]->IsButtonCap = 0
+pp_data->cap[193]->IsPadding = 0
+pp_data->cap[193]->IsAbsolute = 1
+pp_data->cap[193]->IsRange = 0
+pp_data->cap[193]->IsAlias = 0
+pp_data->cap[193]->IsStringRange = 0
+pp_data->cap[193]->IsDesignatorRange = 0
+pp_data->cap[193]->Reserved1 = 0x000000
+pp_data->cap[193]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[193]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[193]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[193]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[193]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[193]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[193]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[193]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[193]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[193]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[193]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[193]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[193]->NotRange.Usage = 0x0081
+pp_data->cap[193]->NotRange.Reserved1 = 0x0081
+pp_data->cap[193]->NotRange.StringIndex = 0
+pp_data->cap[193]->NotRange.Reserved2 = 0
+pp_data->cap[193]->NotRange.DesignatorIndex = 0
+pp_data->cap[193]->NotRange.Reserved3 = 0
+pp_data->cap[193]->NotRange.DataIndex = 106
+pp_data->cap[193]->NotRange.Reserved4 = 106
+pp_data->cap[193]->NotButton.HasNull = 0
+pp_data->cap[193]->NotButton.Reserved4 = 0x000000
+pp_data->cap[193]->NotButton.LogicalMin = 0
+pp_data->cap[193]->NotButton.LogicalMax = 127
+pp_data->cap[193]->NotButton.PhysicalMin = 0
+pp_data->cap[193]->NotButton.PhysicalMax = 0
+pp_data->cap[193]->Units = 0
+pp_data->cap[193]->UnitsExp = 0
+
+pp_data->cap[194]->UsagePage = 0xFF01
+pp_data->cap[194]->ReportID = 0x81
+pp_data->cap[194]->BitPosition = 0
+pp_data->cap[194]->BitSize = 8
+pp_data->cap[194]->ReportCount = 1
+pp_data->cap[194]->BytePosition = 0x001B
+pp_data->cap[194]->BitCount = 8
+pp_data->cap[194]->BitField = 0x02
+pp_data->cap[194]->NextBytePosition = 0x001C
+pp_data->cap[194]->LinkCollection = 0x0004
+pp_data->cap[194]->LinkUsagePage = 0xFF01
+pp_data->cap[194]->LinkUsage = 0x0080
+pp_data->cap[194]->IsMultipleItemsForArray = 0
+pp_data->cap[194]->IsButtonCap = 0
+pp_data->cap[194]->IsPadding = 0
+pp_data->cap[194]->IsAbsolute = 1
+pp_data->cap[194]->IsRange = 0
+pp_data->cap[194]->IsAlias = 0
+pp_data->cap[194]->IsStringRange = 0
+pp_data->cap[194]->IsDesignatorRange = 0
+pp_data->cap[194]->Reserved1 = 0x000000
+pp_data->cap[194]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[194]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[194]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[194]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[194]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[194]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[194]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[194]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[194]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[194]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[194]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[194]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[194]->NotRange.Usage = 0x0081
+pp_data->cap[194]->NotRange.Reserved1 = 0x0081
+pp_data->cap[194]->NotRange.StringIndex = 0
+pp_data->cap[194]->NotRange.Reserved2 = 0
+pp_data->cap[194]->NotRange.DesignatorIndex = 0
+pp_data->cap[194]->NotRange.Reserved3 = 0
+pp_data->cap[194]->NotRange.DataIndex = 107
+pp_data->cap[194]->NotRange.Reserved4 = 107
+pp_data->cap[194]->NotButton.HasNull = 0
+pp_data->cap[194]->NotButton.Reserved4 = 0x000000
+pp_data->cap[194]->NotButton.LogicalMin = 0
+pp_data->cap[194]->NotButton.LogicalMax = 127
+pp_data->cap[194]->NotButton.PhysicalMin = 0
+pp_data->cap[194]->NotButton.PhysicalMax = 0
+pp_data->cap[194]->Units = 0
+pp_data->cap[194]->UnitsExp = 0
+
+pp_data->cap[195]->UsagePage = 0xFF01
+pp_data->cap[195]->ReportID = 0x81
+pp_data->cap[195]->BitPosition = 0
+pp_data->cap[195]->BitSize = 8
+pp_data->cap[195]->ReportCount = 1
+pp_data->cap[195]->BytePosition = 0x001A
+pp_data->cap[195]->BitCount = 8
+pp_data->cap[195]->BitField = 0x02
+pp_data->cap[195]->NextBytePosition = 0x001B
+pp_data->cap[195]->LinkCollection = 0x0004
+pp_data->cap[195]->LinkUsagePage = 0xFF01
+pp_data->cap[195]->LinkUsage = 0x0080
+pp_data->cap[195]->IsMultipleItemsForArray = 0
+pp_data->cap[195]->IsButtonCap = 0
+pp_data->cap[195]->IsPadding = 0
+pp_data->cap[195]->IsAbsolute = 1
+pp_data->cap[195]->IsRange = 0
+pp_data->cap[195]->IsAlias = 0
+pp_data->cap[195]->IsStringRange = 0
+pp_data->cap[195]->IsDesignatorRange = 0
+pp_data->cap[195]->Reserved1 = 0x000000
+pp_data->cap[195]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[195]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[195]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[195]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[195]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[195]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[195]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[195]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[195]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[195]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[195]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[195]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[195]->NotRange.Usage = 0x0081
+pp_data->cap[195]->NotRange.Reserved1 = 0x0081
+pp_data->cap[195]->NotRange.StringIndex = 0
+pp_data->cap[195]->NotRange.Reserved2 = 0
+pp_data->cap[195]->NotRange.DesignatorIndex = 0
+pp_data->cap[195]->NotRange.Reserved3 = 0
+pp_data->cap[195]->NotRange.DataIndex = 108
+pp_data->cap[195]->NotRange.Reserved4 = 108
+pp_data->cap[195]->NotButton.HasNull = 0
+pp_data->cap[195]->NotButton.Reserved4 = 0x000000
+pp_data->cap[195]->NotButton.LogicalMin = 0
+pp_data->cap[195]->NotButton.LogicalMax = 127
+pp_data->cap[195]->NotButton.PhysicalMin = 0
+pp_data->cap[195]->NotButton.PhysicalMax = 0
+pp_data->cap[195]->Units = 0
+pp_data->cap[195]->UnitsExp = 0
+
+pp_data->cap[196]->UsagePage = 0xFF01
+pp_data->cap[196]->ReportID = 0x81
+pp_data->cap[196]->BitPosition = 0
+pp_data->cap[196]->BitSize = 8
+pp_data->cap[196]->ReportCount = 1
+pp_data->cap[196]->BytePosition = 0x0019
+pp_data->cap[196]->BitCount = 8
+pp_data->cap[196]->BitField = 0x02
+pp_data->cap[196]->NextBytePosition = 0x001A
+pp_data->cap[196]->LinkCollection = 0x0004
+pp_data->cap[196]->LinkUsagePage = 0xFF01
+pp_data->cap[196]->LinkUsage = 0x0080
+pp_data->cap[196]->IsMultipleItemsForArray = 0
+pp_data->cap[196]->IsButtonCap = 0
+pp_data->cap[196]->IsPadding = 0
+pp_data->cap[196]->IsAbsolute = 1
+pp_data->cap[196]->IsRange = 0
+pp_data->cap[196]->IsAlias = 0
+pp_data->cap[196]->IsStringRange = 0
+pp_data->cap[196]->IsDesignatorRange = 0
+pp_data->cap[196]->Reserved1 = 0x000000
+pp_data->cap[196]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[196]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[196]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[196]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[196]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[196]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[196]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[196]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[196]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[196]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[196]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[196]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[196]->NotRange.Usage = 0x0081
+pp_data->cap[196]->NotRange.Reserved1 = 0x0081
+pp_data->cap[196]->NotRange.StringIndex = 0
+pp_data->cap[196]->NotRange.Reserved2 = 0
+pp_data->cap[196]->NotRange.DesignatorIndex = 0
+pp_data->cap[196]->NotRange.Reserved3 = 0
+pp_data->cap[196]->NotRange.DataIndex = 109
+pp_data->cap[196]->NotRange.Reserved4 = 109
+pp_data->cap[196]->NotButton.HasNull = 0
+pp_data->cap[196]->NotButton.Reserved4 = 0x000000
+pp_data->cap[196]->NotButton.LogicalMin = 0
+pp_data->cap[196]->NotButton.LogicalMax = 127
+pp_data->cap[196]->NotButton.PhysicalMin = 0
+pp_data->cap[196]->NotButton.PhysicalMax = 0
+pp_data->cap[196]->Units = 0
+pp_data->cap[196]->UnitsExp = 0
+
+pp_data->cap[197]->UsagePage = 0xFF01
+pp_data->cap[197]->ReportID = 0x81
+pp_data->cap[197]->BitPosition = 0
+pp_data->cap[197]->BitSize = 8
+pp_data->cap[197]->ReportCount = 1
+pp_data->cap[197]->BytePosition = 0x0018
+pp_data->cap[197]->BitCount = 8
+pp_data->cap[197]->BitField = 0x02
+pp_data->cap[197]->NextBytePosition = 0x0019
+pp_data->cap[197]->LinkCollection = 0x0004
+pp_data->cap[197]->LinkUsagePage = 0xFF01
+pp_data->cap[197]->LinkUsage = 0x0080
+pp_data->cap[197]->IsMultipleItemsForArray = 0
+pp_data->cap[197]->IsButtonCap = 0
+pp_data->cap[197]->IsPadding = 0
+pp_data->cap[197]->IsAbsolute = 1
+pp_data->cap[197]->IsRange = 0
+pp_data->cap[197]->IsAlias = 0
+pp_data->cap[197]->IsStringRange = 0
+pp_data->cap[197]->IsDesignatorRange = 0
+pp_data->cap[197]->Reserved1 = 0x000000
+pp_data->cap[197]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[197]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[197]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[197]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[197]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[197]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[197]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[197]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[197]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[197]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[197]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[197]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[197]->NotRange.Usage = 0x0081
+pp_data->cap[197]->NotRange.Reserved1 = 0x0081
+pp_data->cap[197]->NotRange.StringIndex = 0
+pp_data->cap[197]->NotRange.Reserved2 = 0
+pp_data->cap[197]->NotRange.DesignatorIndex = 0
+pp_data->cap[197]->NotRange.Reserved3 = 0
+pp_data->cap[197]->NotRange.DataIndex = 110
+pp_data->cap[197]->NotRange.Reserved4 = 110
+pp_data->cap[197]->NotButton.HasNull = 0
+pp_data->cap[197]->NotButton.Reserved4 = 0x000000
+pp_data->cap[197]->NotButton.LogicalMin = 0
+pp_data->cap[197]->NotButton.LogicalMax = 127
+pp_data->cap[197]->NotButton.PhysicalMin = 0
+pp_data->cap[197]->NotButton.PhysicalMax = 0
+pp_data->cap[197]->Units = 0
+pp_data->cap[197]->UnitsExp = 0
+
+pp_data->cap[198]->UsagePage = 0xFF01
+pp_data->cap[198]->ReportID = 0x81
+pp_data->cap[198]->BitPosition = 0
+pp_data->cap[198]->BitSize = 8
+pp_data->cap[198]->ReportCount = 1
+pp_data->cap[198]->BytePosition = 0x0017
+pp_data->cap[198]->BitCount = 8
+pp_data->cap[198]->BitField = 0x02
+pp_data->cap[198]->NextBytePosition = 0x0018
+pp_data->cap[198]->LinkCollection = 0x0004
+pp_data->cap[198]->LinkUsagePage = 0xFF01
+pp_data->cap[198]->LinkUsage = 0x0080
+pp_data->cap[198]->IsMultipleItemsForArray = 0
+pp_data->cap[198]->IsButtonCap = 0
+pp_data->cap[198]->IsPadding = 0
+pp_data->cap[198]->IsAbsolute = 1
+pp_data->cap[198]->IsRange = 0
+pp_data->cap[198]->IsAlias = 0
+pp_data->cap[198]->IsStringRange = 0
+pp_data->cap[198]->IsDesignatorRange = 0
+pp_data->cap[198]->Reserved1 = 0x000000
+pp_data->cap[198]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[198]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[198]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[198]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[198]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[198]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[198]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[198]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[198]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[198]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[198]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[198]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[198]->NotRange.Usage = 0x0081
+pp_data->cap[198]->NotRange.Reserved1 = 0x0081
+pp_data->cap[198]->NotRange.StringIndex = 0
+pp_data->cap[198]->NotRange.Reserved2 = 0
+pp_data->cap[198]->NotRange.DesignatorIndex = 0
+pp_data->cap[198]->NotRange.Reserved3 = 0
+pp_data->cap[198]->NotRange.DataIndex = 111
+pp_data->cap[198]->NotRange.Reserved4 = 111
+pp_data->cap[198]->NotButton.HasNull = 0
+pp_data->cap[198]->NotButton.Reserved4 = 0x000000
+pp_data->cap[198]->NotButton.LogicalMin = 0
+pp_data->cap[198]->NotButton.LogicalMax = 127
+pp_data->cap[198]->NotButton.PhysicalMin = 0
+pp_data->cap[198]->NotButton.PhysicalMax = 0
+pp_data->cap[198]->Units = 0
+pp_data->cap[198]->UnitsExp = 0
+
+pp_data->cap[199]->UsagePage = 0xFF01
+pp_data->cap[199]->ReportID = 0x81
+pp_data->cap[199]->BitPosition = 0
+pp_data->cap[199]->BitSize = 8
+pp_data->cap[199]->ReportCount = 1
+pp_data->cap[199]->BytePosition = 0x0016
+pp_data->cap[199]->BitCount = 8
+pp_data->cap[199]->BitField = 0x02
+pp_data->cap[199]->NextBytePosition = 0x0017
+pp_data->cap[199]->LinkCollection = 0x0004
+pp_data->cap[199]->LinkUsagePage = 0xFF01
+pp_data->cap[199]->LinkUsage = 0x0080
+pp_data->cap[199]->IsMultipleItemsForArray = 0
+pp_data->cap[199]->IsButtonCap = 0
+pp_data->cap[199]->IsPadding = 0
+pp_data->cap[199]->IsAbsolute = 1
+pp_data->cap[199]->IsRange = 0
+pp_data->cap[199]->IsAlias = 0
+pp_data->cap[199]->IsStringRange = 0
+pp_data->cap[199]->IsDesignatorRange = 0
+pp_data->cap[199]->Reserved1 = 0x000000
+pp_data->cap[199]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[199]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[199]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[199]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[199]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[199]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[199]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[199]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[199]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[199]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[199]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[199]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[199]->NotRange.Usage = 0x0081
+pp_data->cap[199]->NotRange.Reserved1 = 0x0081
+pp_data->cap[199]->NotRange.StringIndex = 0
+pp_data->cap[199]->NotRange.Reserved2 = 0
+pp_data->cap[199]->NotRange.DesignatorIndex = 0
+pp_data->cap[199]->NotRange.Reserved3 = 0
+pp_data->cap[199]->NotRange.DataIndex = 112
+pp_data->cap[199]->NotRange.Reserved4 = 112
+pp_data->cap[199]->NotButton.HasNull = 0
+pp_data->cap[199]->NotButton.Reserved4 = 0x000000
+pp_data->cap[199]->NotButton.LogicalMin = 0
+pp_data->cap[199]->NotButton.LogicalMax = 127
+pp_data->cap[199]->NotButton.PhysicalMin = 0
+pp_data->cap[199]->NotButton.PhysicalMax = 0
+pp_data->cap[199]->Units = 0
+pp_data->cap[199]->UnitsExp = 0
+
+pp_data->cap[200]->UsagePage = 0xFF01
+pp_data->cap[200]->ReportID = 0x81
+pp_data->cap[200]->BitPosition = 0
+pp_data->cap[200]->BitSize = 8
+pp_data->cap[200]->ReportCount = 1
+pp_data->cap[200]->BytePosition = 0x0015
+pp_data->cap[200]->BitCount = 8
+pp_data->cap[200]->BitField = 0x02
+pp_data->cap[200]->NextBytePosition = 0x0016
+pp_data->cap[200]->LinkCollection = 0x0004
+pp_data->cap[200]->LinkUsagePage = 0xFF01
+pp_data->cap[200]->LinkUsage = 0x0080
+pp_data->cap[200]->IsMultipleItemsForArray = 0
+pp_data->cap[200]->IsButtonCap = 0
+pp_data->cap[200]->IsPadding = 0
+pp_data->cap[200]->IsAbsolute = 1
+pp_data->cap[200]->IsRange = 0
+pp_data->cap[200]->IsAlias = 0
+pp_data->cap[200]->IsStringRange = 0
+pp_data->cap[200]->IsDesignatorRange = 0
+pp_data->cap[200]->Reserved1 = 0x000000
+pp_data->cap[200]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[200]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[200]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[200]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[200]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[200]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[200]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[200]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[200]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[200]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[200]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[200]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[200]->NotRange.Usage = 0x0081
+pp_data->cap[200]->NotRange.Reserved1 = 0x0081
+pp_data->cap[200]->NotRange.StringIndex = 0
+pp_data->cap[200]->NotRange.Reserved2 = 0
+pp_data->cap[200]->NotRange.DesignatorIndex = 0
+pp_data->cap[200]->NotRange.Reserved3 = 0
+pp_data->cap[200]->NotRange.DataIndex = 113
+pp_data->cap[200]->NotRange.Reserved4 = 113
+pp_data->cap[200]->NotButton.HasNull = 0
+pp_data->cap[200]->NotButton.Reserved4 = 0x000000
+pp_data->cap[200]->NotButton.LogicalMin = 0
+pp_data->cap[200]->NotButton.LogicalMax = 127
+pp_data->cap[200]->NotButton.PhysicalMin = 0
+pp_data->cap[200]->NotButton.PhysicalMax = 0
+pp_data->cap[200]->Units = 0
+pp_data->cap[200]->UnitsExp = 0
+
+pp_data->cap[201]->UsagePage = 0xFF01
+pp_data->cap[201]->ReportID = 0x81
+pp_data->cap[201]->BitPosition = 0
+pp_data->cap[201]->BitSize = 8
+pp_data->cap[201]->ReportCount = 1
+pp_data->cap[201]->BytePosition = 0x0014
+pp_data->cap[201]->BitCount = 8
+pp_data->cap[201]->BitField = 0x02
+pp_data->cap[201]->NextBytePosition = 0x0015
+pp_data->cap[201]->LinkCollection = 0x0004
+pp_data->cap[201]->LinkUsagePage = 0xFF01
+pp_data->cap[201]->LinkUsage = 0x0080
+pp_data->cap[201]->IsMultipleItemsForArray = 0
+pp_data->cap[201]->IsButtonCap = 0
+pp_data->cap[201]->IsPadding = 0
+pp_data->cap[201]->IsAbsolute = 1
+pp_data->cap[201]->IsRange = 0
+pp_data->cap[201]->IsAlias = 0
+pp_data->cap[201]->IsStringRange = 0
+pp_data->cap[201]->IsDesignatorRange = 0
+pp_data->cap[201]->Reserved1 = 0x000000
+pp_data->cap[201]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[201]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[201]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[201]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[201]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[201]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[201]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[201]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[201]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[201]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[201]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[201]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[201]->NotRange.Usage = 0x0081
+pp_data->cap[201]->NotRange.Reserved1 = 0x0081
+pp_data->cap[201]->NotRange.StringIndex = 0
+pp_data->cap[201]->NotRange.Reserved2 = 0
+pp_data->cap[201]->NotRange.DesignatorIndex = 0
+pp_data->cap[201]->NotRange.Reserved3 = 0
+pp_data->cap[201]->NotRange.DataIndex = 114
+pp_data->cap[201]->NotRange.Reserved4 = 114
+pp_data->cap[201]->NotButton.HasNull = 0
+pp_data->cap[201]->NotButton.Reserved4 = 0x000000
+pp_data->cap[201]->NotButton.LogicalMin = 0
+pp_data->cap[201]->NotButton.LogicalMax = 127
+pp_data->cap[201]->NotButton.PhysicalMin = 0
+pp_data->cap[201]->NotButton.PhysicalMax = 0
+pp_data->cap[201]->Units = 0
+pp_data->cap[201]->UnitsExp = 0
+
+pp_data->cap[202]->UsagePage = 0xFF01
+pp_data->cap[202]->ReportID = 0x81
+pp_data->cap[202]->BitPosition = 0
+pp_data->cap[202]->BitSize = 8
+pp_data->cap[202]->ReportCount = 1
+pp_data->cap[202]->BytePosition = 0x0013
+pp_data->cap[202]->BitCount = 8
+pp_data->cap[202]->BitField = 0x02
+pp_data->cap[202]->NextBytePosition = 0x0014
+pp_data->cap[202]->LinkCollection = 0x0004
+pp_data->cap[202]->LinkUsagePage = 0xFF01
+pp_data->cap[202]->LinkUsage = 0x0080
+pp_data->cap[202]->IsMultipleItemsForArray = 0
+pp_data->cap[202]->IsButtonCap = 0
+pp_data->cap[202]->IsPadding = 0
+pp_data->cap[202]->IsAbsolute = 1
+pp_data->cap[202]->IsRange = 0
+pp_data->cap[202]->IsAlias = 0
+pp_data->cap[202]->IsStringRange = 0
+pp_data->cap[202]->IsDesignatorRange = 0
+pp_data->cap[202]->Reserved1 = 0x000000
+pp_data->cap[202]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[202]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[202]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[202]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[202]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[202]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[202]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[202]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[202]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[202]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[202]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[202]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[202]->NotRange.Usage = 0x0081
+pp_data->cap[202]->NotRange.Reserved1 = 0x0081
+pp_data->cap[202]->NotRange.StringIndex = 0
+pp_data->cap[202]->NotRange.Reserved2 = 0
+pp_data->cap[202]->NotRange.DesignatorIndex = 0
+pp_data->cap[202]->NotRange.Reserved3 = 0
+pp_data->cap[202]->NotRange.DataIndex = 115
+pp_data->cap[202]->NotRange.Reserved4 = 115
+pp_data->cap[202]->NotButton.HasNull = 0
+pp_data->cap[202]->NotButton.Reserved4 = 0x000000
+pp_data->cap[202]->NotButton.LogicalMin = 0
+pp_data->cap[202]->NotButton.LogicalMax = 127
+pp_data->cap[202]->NotButton.PhysicalMin = 0
+pp_data->cap[202]->NotButton.PhysicalMax = 0
+pp_data->cap[202]->Units = 0
+pp_data->cap[202]->UnitsExp = 0
+
+pp_data->cap[203]->UsagePage = 0xFF01
+pp_data->cap[203]->ReportID = 0x81
+pp_data->cap[203]->BitPosition = 0
+pp_data->cap[203]->BitSize = 8
+pp_data->cap[203]->ReportCount = 1
+pp_data->cap[203]->BytePosition = 0x0012
+pp_data->cap[203]->BitCount = 8
+pp_data->cap[203]->BitField = 0x02
+pp_data->cap[203]->NextBytePosition = 0x0013
+pp_data->cap[203]->LinkCollection = 0x0004
+pp_data->cap[203]->LinkUsagePage = 0xFF01
+pp_data->cap[203]->LinkUsage = 0x0080
+pp_data->cap[203]->IsMultipleItemsForArray = 0
+pp_data->cap[203]->IsButtonCap = 0
+pp_data->cap[203]->IsPadding = 0
+pp_data->cap[203]->IsAbsolute = 1
+pp_data->cap[203]->IsRange = 0
+pp_data->cap[203]->IsAlias = 0
+pp_data->cap[203]->IsStringRange = 0
+pp_data->cap[203]->IsDesignatorRange = 0
+pp_data->cap[203]->Reserved1 = 0x000000
+pp_data->cap[203]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[203]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[203]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[203]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[203]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[203]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[203]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[203]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[203]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[203]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[203]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[203]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[203]->NotRange.Usage = 0x0081
+pp_data->cap[203]->NotRange.Reserved1 = 0x0081
+pp_data->cap[203]->NotRange.StringIndex = 0
+pp_data->cap[203]->NotRange.Reserved2 = 0
+pp_data->cap[203]->NotRange.DesignatorIndex = 0
+pp_data->cap[203]->NotRange.Reserved3 = 0
+pp_data->cap[203]->NotRange.DataIndex = 116
+pp_data->cap[203]->NotRange.Reserved4 = 116
+pp_data->cap[203]->NotButton.HasNull = 0
+pp_data->cap[203]->NotButton.Reserved4 = 0x000000
+pp_data->cap[203]->NotButton.LogicalMin = 0
+pp_data->cap[203]->NotButton.LogicalMax = 127
+pp_data->cap[203]->NotButton.PhysicalMin = 0
+pp_data->cap[203]->NotButton.PhysicalMax = 0
+pp_data->cap[203]->Units = 0
+pp_data->cap[203]->UnitsExp = 0
+
+pp_data->cap[204]->UsagePage = 0xFF01
+pp_data->cap[204]->ReportID = 0x81
+pp_data->cap[204]->BitPosition = 0
+pp_data->cap[204]->BitSize = 8
+pp_data->cap[204]->ReportCount = 1
+pp_data->cap[204]->BytePosition = 0x0011
+pp_data->cap[204]->BitCount = 8
+pp_data->cap[204]->BitField = 0x02
+pp_data->cap[204]->NextBytePosition = 0x0012
+pp_data->cap[204]->LinkCollection = 0x0004
+pp_data->cap[204]->LinkUsagePage = 0xFF01
+pp_data->cap[204]->LinkUsage = 0x0080
+pp_data->cap[204]->IsMultipleItemsForArray = 0
+pp_data->cap[204]->IsButtonCap = 0
+pp_data->cap[204]->IsPadding = 0
+pp_data->cap[204]->IsAbsolute = 1
+pp_data->cap[204]->IsRange = 0
+pp_data->cap[204]->IsAlias = 0
+pp_data->cap[204]->IsStringRange = 0
+pp_data->cap[204]->IsDesignatorRange = 0
+pp_data->cap[204]->Reserved1 = 0x000000
+pp_data->cap[204]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[204]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[204]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[204]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[204]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[204]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[204]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[204]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[204]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[204]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[204]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[204]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[204]->NotRange.Usage = 0x0081
+pp_data->cap[204]->NotRange.Reserved1 = 0x0081
+pp_data->cap[204]->NotRange.StringIndex = 0
+pp_data->cap[204]->NotRange.Reserved2 = 0
+pp_data->cap[204]->NotRange.DesignatorIndex = 0
+pp_data->cap[204]->NotRange.Reserved3 = 0
+pp_data->cap[204]->NotRange.DataIndex = 117
+pp_data->cap[204]->NotRange.Reserved4 = 117
+pp_data->cap[204]->NotButton.HasNull = 0
+pp_data->cap[204]->NotButton.Reserved4 = 0x000000
+pp_data->cap[204]->NotButton.LogicalMin = 0
+pp_data->cap[204]->NotButton.LogicalMax = 127
+pp_data->cap[204]->NotButton.PhysicalMin = 0
+pp_data->cap[204]->NotButton.PhysicalMax = 0
+pp_data->cap[204]->Units = 0
+pp_data->cap[204]->UnitsExp = 0
+
+pp_data->cap[205]->UsagePage = 0xFF01
+pp_data->cap[205]->ReportID = 0x81
+pp_data->cap[205]->BitPosition = 0
+pp_data->cap[205]->BitSize = 8
+pp_data->cap[205]->ReportCount = 1
+pp_data->cap[205]->BytePosition = 0x0010
+pp_data->cap[205]->BitCount = 8
+pp_data->cap[205]->BitField = 0x02
+pp_data->cap[205]->NextBytePosition = 0x0011
+pp_data->cap[205]->LinkCollection = 0x0004
+pp_data->cap[205]->LinkUsagePage = 0xFF01
+pp_data->cap[205]->LinkUsage = 0x0080
+pp_data->cap[205]->IsMultipleItemsForArray = 0
+pp_data->cap[205]->IsButtonCap = 0
+pp_data->cap[205]->IsPadding = 0
+pp_data->cap[205]->IsAbsolute = 1
+pp_data->cap[205]->IsRange = 0
+pp_data->cap[205]->IsAlias = 0
+pp_data->cap[205]->IsStringRange = 0
+pp_data->cap[205]->IsDesignatorRange = 0
+pp_data->cap[205]->Reserved1 = 0x000000
+pp_data->cap[205]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[205]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[205]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[205]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[205]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[205]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[205]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[205]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[205]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[205]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[205]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[205]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[205]->NotRange.Usage = 0x0081
+pp_data->cap[205]->NotRange.Reserved1 = 0x0081
+pp_data->cap[205]->NotRange.StringIndex = 0
+pp_data->cap[205]->NotRange.Reserved2 = 0
+pp_data->cap[205]->NotRange.DesignatorIndex = 0
+pp_data->cap[205]->NotRange.Reserved3 = 0
+pp_data->cap[205]->NotRange.DataIndex = 118
+pp_data->cap[205]->NotRange.Reserved4 = 118
+pp_data->cap[205]->NotButton.HasNull = 0
+pp_data->cap[205]->NotButton.Reserved4 = 0x000000
+pp_data->cap[205]->NotButton.LogicalMin = 0
+pp_data->cap[205]->NotButton.LogicalMax = 127
+pp_data->cap[205]->NotButton.PhysicalMin = 0
+pp_data->cap[205]->NotButton.PhysicalMax = 0
+pp_data->cap[205]->Units = 0
+pp_data->cap[205]->UnitsExp = 0
+
+pp_data->cap[206]->UsagePage = 0xFF01
+pp_data->cap[206]->ReportID = 0x81
+pp_data->cap[206]->BitPosition = 0
+pp_data->cap[206]->BitSize = 8
+pp_data->cap[206]->ReportCount = 1
+pp_data->cap[206]->BytePosition = 0x000F
+pp_data->cap[206]->BitCount = 8
+pp_data->cap[206]->BitField = 0x02
+pp_data->cap[206]->NextBytePosition = 0x0010
+pp_data->cap[206]->LinkCollection = 0x0004
+pp_data->cap[206]->LinkUsagePage = 0xFF01
+pp_data->cap[206]->LinkUsage = 0x0080
+pp_data->cap[206]->IsMultipleItemsForArray = 0
+pp_data->cap[206]->IsButtonCap = 0
+pp_data->cap[206]->IsPadding = 0
+pp_data->cap[206]->IsAbsolute = 1
+pp_data->cap[206]->IsRange = 0
+pp_data->cap[206]->IsAlias = 0
+pp_data->cap[206]->IsStringRange = 0
+pp_data->cap[206]->IsDesignatorRange = 0
+pp_data->cap[206]->Reserved1 = 0x000000
+pp_data->cap[206]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[206]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[206]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[206]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[206]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[206]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[206]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[206]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[206]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[206]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[206]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[206]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[206]->NotRange.Usage = 0x0081
+pp_data->cap[206]->NotRange.Reserved1 = 0x0081
+pp_data->cap[206]->NotRange.StringIndex = 0
+pp_data->cap[206]->NotRange.Reserved2 = 0
+pp_data->cap[206]->NotRange.DesignatorIndex = 0
+pp_data->cap[206]->NotRange.Reserved3 = 0
+pp_data->cap[206]->NotRange.DataIndex = 119
+pp_data->cap[206]->NotRange.Reserved4 = 119
+pp_data->cap[206]->NotButton.HasNull = 0
+pp_data->cap[206]->NotButton.Reserved4 = 0x000000
+pp_data->cap[206]->NotButton.LogicalMin = 0
+pp_data->cap[206]->NotButton.LogicalMax = 127
+pp_data->cap[206]->NotButton.PhysicalMin = 0
+pp_data->cap[206]->NotButton.PhysicalMax = 0
+pp_data->cap[206]->Units = 0
+pp_data->cap[206]->UnitsExp = 0
+
+pp_data->cap[207]->UsagePage = 0xFF01
+pp_data->cap[207]->ReportID = 0x81
+pp_data->cap[207]->BitPosition = 0
+pp_data->cap[207]->BitSize = 8
+pp_data->cap[207]->ReportCount = 1
+pp_data->cap[207]->BytePosition = 0x000E
+pp_data->cap[207]->BitCount = 8
+pp_data->cap[207]->BitField = 0x02
+pp_data->cap[207]->NextBytePosition = 0x000F
+pp_data->cap[207]->LinkCollection = 0x0004
+pp_data->cap[207]->LinkUsagePage = 0xFF01
+pp_data->cap[207]->LinkUsage = 0x0080
+pp_data->cap[207]->IsMultipleItemsForArray = 0
+pp_data->cap[207]->IsButtonCap = 0
+pp_data->cap[207]->IsPadding = 0
+pp_data->cap[207]->IsAbsolute = 1
+pp_data->cap[207]->IsRange = 0
+pp_data->cap[207]->IsAlias = 0
+pp_data->cap[207]->IsStringRange = 0
+pp_data->cap[207]->IsDesignatorRange = 0
+pp_data->cap[207]->Reserved1 = 0x000000
+pp_data->cap[207]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[207]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[207]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[207]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[207]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[207]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[207]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[207]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[207]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[207]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[207]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[207]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[207]->NotRange.Usage = 0x0081
+pp_data->cap[207]->NotRange.Reserved1 = 0x0081
+pp_data->cap[207]->NotRange.StringIndex = 0
+pp_data->cap[207]->NotRange.Reserved2 = 0
+pp_data->cap[207]->NotRange.DesignatorIndex = 0
+pp_data->cap[207]->NotRange.Reserved3 = 0
+pp_data->cap[207]->NotRange.DataIndex = 120
+pp_data->cap[207]->NotRange.Reserved4 = 120
+pp_data->cap[207]->NotButton.HasNull = 0
+pp_data->cap[207]->NotButton.Reserved4 = 0x000000
+pp_data->cap[207]->NotButton.LogicalMin = 0
+pp_data->cap[207]->NotButton.LogicalMax = 127
+pp_data->cap[207]->NotButton.PhysicalMin = 0
+pp_data->cap[207]->NotButton.PhysicalMax = 0
+pp_data->cap[207]->Units = 0
+pp_data->cap[207]->UnitsExp = 0
+
+pp_data->cap[208]->UsagePage = 0xFF01
+pp_data->cap[208]->ReportID = 0x81
+pp_data->cap[208]->BitPosition = 0
+pp_data->cap[208]->BitSize = 8
+pp_data->cap[208]->ReportCount = 1
+pp_data->cap[208]->BytePosition = 0x000D
+pp_data->cap[208]->BitCount = 8
+pp_data->cap[208]->BitField = 0x02
+pp_data->cap[208]->NextBytePosition = 0x000E
+pp_data->cap[208]->LinkCollection = 0x0004
+pp_data->cap[208]->LinkUsagePage = 0xFF01
+pp_data->cap[208]->LinkUsage = 0x0080
+pp_data->cap[208]->IsMultipleItemsForArray = 0
+pp_data->cap[208]->IsButtonCap = 0
+pp_data->cap[208]->IsPadding = 0
+pp_data->cap[208]->IsAbsolute = 1
+pp_data->cap[208]->IsRange = 0
+pp_data->cap[208]->IsAlias = 0
+pp_data->cap[208]->IsStringRange = 0
+pp_data->cap[208]->IsDesignatorRange = 0
+pp_data->cap[208]->Reserved1 = 0x000000
+pp_data->cap[208]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[208]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[208]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[208]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[208]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[208]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[208]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[208]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[208]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[208]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[208]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[208]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[208]->NotRange.Usage = 0x0081
+pp_data->cap[208]->NotRange.Reserved1 = 0x0081
+pp_data->cap[208]->NotRange.StringIndex = 0
+pp_data->cap[208]->NotRange.Reserved2 = 0
+pp_data->cap[208]->NotRange.DesignatorIndex = 0
+pp_data->cap[208]->NotRange.Reserved3 = 0
+pp_data->cap[208]->NotRange.DataIndex = 121
+pp_data->cap[208]->NotRange.Reserved4 = 121
+pp_data->cap[208]->NotButton.HasNull = 0
+pp_data->cap[208]->NotButton.Reserved4 = 0x000000
+pp_data->cap[208]->NotButton.LogicalMin = 0
+pp_data->cap[208]->NotButton.LogicalMax = 127
+pp_data->cap[208]->NotButton.PhysicalMin = 0
+pp_data->cap[208]->NotButton.PhysicalMax = 0
+pp_data->cap[208]->Units = 0
+pp_data->cap[208]->UnitsExp = 0
+
+pp_data->cap[209]->UsagePage = 0xFF01
+pp_data->cap[209]->ReportID = 0x81
+pp_data->cap[209]->BitPosition = 0
+pp_data->cap[209]->BitSize = 8
+pp_data->cap[209]->ReportCount = 1
+pp_data->cap[209]->BytePosition = 0x000C
+pp_data->cap[209]->BitCount = 8
+pp_data->cap[209]->BitField = 0x02
+pp_data->cap[209]->NextBytePosition = 0x000D
+pp_data->cap[209]->LinkCollection = 0x0004
+pp_data->cap[209]->LinkUsagePage = 0xFF01
+pp_data->cap[209]->LinkUsage = 0x0080
+pp_data->cap[209]->IsMultipleItemsForArray = 0
+pp_data->cap[209]->IsButtonCap = 0
+pp_data->cap[209]->IsPadding = 0
+pp_data->cap[209]->IsAbsolute = 1
+pp_data->cap[209]->IsRange = 0
+pp_data->cap[209]->IsAlias = 0
+pp_data->cap[209]->IsStringRange = 0
+pp_data->cap[209]->IsDesignatorRange = 0
+pp_data->cap[209]->Reserved1 = 0x000000
+pp_data->cap[209]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[209]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[209]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[209]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[209]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[209]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[209]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[209]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[209]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[209]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[209]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[209]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[209]->NotRange.Usage = 0x0081
+pp_data->cap[209]->NotRange.Reserved1 = 0x0081
+pp_data->cap[209]->NotRange.StringIndex = 0
+pp_data->cap[209]->NotRange.Reserved2 = 0
+pp_data->cap[209]->NotRange.DesignatorIndex = 0
+pp_data->cap[209]->NotRange.Reserved3 = 0
+pp_data->cap[209]->NotRange.DataIndex = 122
+pp_data->cap[209]->NotRange.Reserved4 = 122
+pp_data->cap[209]->NotButton.HasNull = 0
+pp_data->cap[209]->NotButton.Reserved4 = 0x000000
+pp_data->cap[209]->NotButton.LogicalMin = 0
+pp_data->cap[209]->NotButton.LogicalMax = 127
+pp_data->cap[209]->NotButton.PhysicalMin = 0
+pp_data->cap[209]->NotButton.PhysicalMax = 0
+pp_data->cap[209]->Units = 0
+pp_data->cap[209]->UnitsExp = 0
+
+pp_data->cap[210]->UsagePage = 0xFF01
+pp_data->cap[210]->ReportID = 0x81
+pp_data->cap[210]->BitPosition = 0
+pp_data->cap[210]->BitSize = 8
+pp_data->cap[210]->ReportCount = 1
+pp_data->cap[210]->BytePosition = 0x000B
+pp_data->cap[210]->BitCount = 8
+pp_data->cap[210]->BitField = 0x02
+pp_data->cap[210]->NextBytePosition = 0x000C
+pp_data->cap[210]->LinkCollection = 0x0004
+pp_data->cap[210]->LinkUsagePage = 0xFF01
+pp_data->cap[210]->LinkUsage = 0x0080
+pp_data->cap[210]->IsMultipleItemsForArray = 0
+pp_data->cap[210]->IsButtonCap = 0
+pp_data->cap[210]->IsPadding = 0
+pp_data->cap[210]->IsAbsolute = 1
+pp_data->cap[210]->IsRange = 0
+pp_data->cap[210]->IsAlias = 0
+pp_data->cap[210]->IsStringRange = 0
+pp_data->cap[210]->IsDesignatorRange = 0
+pp_data->cap[210]->Reserved1 = 0x000000
+pp_data->cap[210]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[210]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[210]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[210]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[210]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[210]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[210]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[210]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[210]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[210]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[210]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[210]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[210]->NotRange.Usage = 0x0081
+pp_data->cap[210]->NotRange.Reserved1 = 0x0081
+pp_data->cap[210]->NotRange.StringIndex = 0
+pp_data->cap[210]->NotRange.Reserved2 = 0
+pp_data->cap[210]->NotRange.DesignatorIndex = 0
+pp_data->cap[210]->NotRange.Reserved3 = 0
+pp_data->cap[210]->NotRange.DataIndex = 123
+pp_data->cap[210]->NotRange.Reserved4 = 123
+pp_data->cap[210]->NotButton.HasNull = 0
+pp_data->cap[210]->NotButton.Reserved4 = 0x000000
+pp_data->cap[210]->NotButton.LogicalMin = 0
+pp_data->cap[210]->NotButton.LogicalMax = 127
+pp_data->cap[210]->NotButton.PhysicalMin = 0
+pp_data->cap[210]->NotButton.PhysicalMax = 0
+pp_data->cap[210]->Units = 0
+pp_data->cap[210]->UnitsExp = 0
+
+pp_data->cap[211]->UsagePage = 0xFF01
+pp_data->cap[211]->ReportID = 0x81
+pp_data->cap[211]->BitPosition = 0
+pp_data->cap[211]->BitSize = 8
+pp_data->cap[211]->ReportCount = 1
+pp_data->cap[211]->BytePosition = 0x000A
+pp_data->cap[211]->BitCount = 8
+pp_data->cap[211]->BitField = 0x02
+pp_data->cap[211]->NextBytePosition = 0x000B
+pp_data->cap[211]->LinkCollection = 0x0004
+pp_data->cap[211]->LinkUsagePage = 0xFF01
+pp_data->cap[211]->LinkUsage = 0x0080
+pp_data->cap[211]->IsMultipleItemsForArray = 0
+pp_data->cap[211]->IsButtonCap = 0
+pp_data->cap[211]->IsPadding = 0
+pp_data->cap[211]->IsAbsolute = 1
+pp_data->cap[211]->IsRange = 0
+pp_data->cap[211]->IsAlias = 0
+pp_data->cap[211]->IsStringRange = 0
+pp_data->cap[211]->IsDesignatorRange = 0
+pp_data->cap[211]->Reserved1 = 0x000000
+pp_data->cap[211]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[211]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[211]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[211]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[211]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[211]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[211]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[211]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[211]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[211]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[211]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[211]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[211]->NotRange.Usage = 0x0081
+pp_data->cap[211]->NotRange.Reserved1 = 0x0081
+pp_data->cap[211]->NotRange.StringIndex = 0
+pp_data->cap[211]->NotRange.Reserved2 = 0
+pp_data->cap[211]->NotRange.DesignatorIndex = 0
+pp_data->cap[211]->NotRange.Reserved3 = 0
+pp_data->cap[211]->NotRange.DataIndex = 124
+pp_data->cap[211]->NotRange.Reserved4 = 124
+pp_data->cap[211]->NotButton.HasNull = 0
+pp_data->cap[211]->NotButton.Reserved4 = 0x000000
+pp_data->cap[211]->NotButton.LogicalMin = 0
+pp_data->cap[211]->NotButton.LogicalMax = 127
+pp_data->cap[211]->NotButton.PhysicalMin = 0
+pp_data->cap[211]->NotButton.PhysicalMax = 0
+pp_data->cap[211]->Units = 0
+pp_data->cap[211]->UnitsExp = 0
+
+pp_data->cap[212]->UsagePage = 0xFF01
+pp_data->cap[212]->ReportID = 0x81
+pp_data->cap[212]->BitPosition = 0
+pp_data->cap[212]->BitSize = 8
+pp_data->cap[212]->ReportCount = 1
+pp_data->cap[212]->BytePosition = 0x0009
+pp_data->cap[212]->BitCount = 8
+pp_data->cap[212]->BitField = 0x02
+pp_data->cap[212]->NextBytePosition = 0x000A
+pp_data->cap[212]->LinkCollection = 0x0004
+pp_data->cap[212]->LinkUsagePage = 0xFF01
+pp_data->cap[212]->LinkUsage = 0x0080
+pp_data->cap[212]->IsMultipleItemsForArray = 0
+pp_data->cap[212]->IsButtonCap = 0
+pp_data->cap[212]->IsPadding = 0
+pp_data->cap[212]->IsAbsolute = 1
+pp_data->cap[212]->IsRange = 0
+pp_data->cap[212]->IsAlias = 0
+pp_data->cap[212]->IsStringRange = 0
+pp_data->cap[212]->IsDesignatorRange = 0
+pp_data->cap[212]->Reserved1 = 0x000000
+pp_data->cap[212]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[212]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[212]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[212]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[212]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[212]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[212]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[212]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[212]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[212]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[212]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[212]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[212]->NotRange.Usage = 0x0081
+pp_data->cap[212]->NotRange.Reserved1 = 0x0081
+pp_data->cap[212]->NotRange.StringIndex = 0
+pp_data->cap[212]->NotRange.Reserved2 = 0
+pp_data->cap[212]->NotRange.DesignatorIndex = 0
+pp_data->cap[212]->NotRange.Reserved3 = 0
+pp_data->cap[212]->NotRange.DataIndex = 125
+pp_data->cap[212]->NotRange.Reserved4 = 125
+pp_data->cap[212]->NotButton.HasNull = 0
+pp_data->cap[212]->NotButton.Reserved4 = 0x000000
+pp_data->cap[212]->NotButton.LogicalMin = 0
+pp_data->cap[212]->NotButton.LogicalMax = 127
+pp_data->cap[212]->NotButton.PhysicalMin = 0
+pp_data->cap[212]->NotButton.PhysicalMax = 0
+pp_data->cap[212]->Units = 0
+pp_data->cap[212]->UnitsExp = 0
+
+pp_data->cap[213]->UsagePage = 0xFF01
+pp_data->cap[213]->ReportID = 0x81
+pp_data->cap[213]->BitPosition = 0
+pp_data->cap[213]->BitSize = 8
+pp_data->cap[213]->ReportCount = 1
+pp_data->cap[213]->BytePosition = 0x0008
+pp_data->cap[213]->BitCount = 8
+pp_data->cap[213]->BitField = 0x02
+pp_data->cap[213]->NextBytePosition = 0x0009
+pp_data->cap[213]->LinkCollection = 0x0004
+pp_data->cap[213]->LinkUsagePage = 0xFF01
+pp_data->cap[213]->LinkUsage = 0x0080
+pp_data->cap[213]->IsMultipleItemsForArray = 0
+pp_data->cap[213]->IsButtonCap = 0
+pp_data->cap[213]->IsPadding = 0
+pp_data->cap[213]->IsAbsolute = 1
+pp_data->cap[213]->IsRange = 0
+pp_data->cap[213]->IsAlias = 0
+pp_data->cap[213]->IsStringRange = 0
+pp_data->cap[213]->IsDesignatorRange = 0
+pp_data->cap[213]->Reserved1 = 0x000000
+pp_data->cap[213]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[213]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[213]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[213]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[213]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[213]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[213]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[213]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[213]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[213]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[213]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[213]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[213]->NotRange.Usage = 0x0081
+pp_data->cap[213]->NotRange.Reserved1 = 0x0081
+pp_data->cap[213]->NotRange.StringIndex = 0
+pp_data->cap[213]->NotRange.Reserved2 = 0
+pp_data->cap[213]->NotRange.DesignatorIndex = 0
+pp_data->cap[213]->NotRange.Reserved3 = 0
+pp_data->cap[213]->NotRange.DataIndex = 126
+pp_data->cap[213]->NotRange.Reserved4 = 126
+pp_data->cap[213]->NotButton.HasNull = 0
+pp_data->cap[213]->NotButton.Reserved4 = 0x000000
+pp_data->cap[213]->NotButton.LogicalMin = 0
+pp_data->cap[213]->NotButton.LogicalMax = 127
+pp_data->cap[213]->NotButton.PhysicalMin = 0
+pp_data->cap[213]->NotButton.PhysicalMax = 0
+pp_data->cap[213]->Units = 0
+pp_data->cap[213]->UnitsExp = 0
+
+pp_data->cap[214]->UsagePage = 0xFF01
+pp_data->cap[214]->ReportID = 0x81
+pp_data->cap[214]->BitPosition = 0
+pp_data->cap[214]->BitSize = 8
+pp_data->cap[214]->ReportCount = 1
+pp_data->cap[214]->BytePosition = 0x0007
+pp_data->cap[214]->BitCount = 8
+pp_data->cap[214]->BitField = 0x02
+pp_data->cap[214]->NextBytePosition = 0x0008
+pp_data->cap[214]->LinkCollection = 0x0004
+pp_data->cap[214]->LinkUsagePage = 0xFF01
+pp_data->cap[214]->LinkUsage = 0x0080
+pp_data->cap[214]->IsMultipleItemsForArray = 0
+pp_data->cap[214]->IsButtonCap = 0
+pp_data->cap[214]->IsPadding = 0
+pp_data->cap[214]->IsAbsolute = 1
+pp_data->cap[214]->IsRange = 0
+pp_data->cap[214]->IsAlias = 0
+pp_data->cap[214]->IsStringRange = 0
+pp_data->cap[214]->IsDesignatorRange = 0
+pp_data->cap[214]->Reserved1 = 0x000000
+pp_data->cap[214]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[214]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[214]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[214]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[214]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[214]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[214]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[214]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[214]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[214]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[214]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[214]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[214]->NotRange.Usage = 0x0081
+pp_data->cap[214]->NotRange.Reserved1 = 0x0081
+pp_data->cap[214]->NotRange.StringIndex = 0
+pp_data->cap[214]->NotRange.Reserved2 = 0
+pp_data->cap[214]->NotRange.DesignatorIndex = 0
+pp_data->cap[214]->NotRange.Reserved3 = 0
+pp_data->cap[214]->NotRange.DataIndex = 127
+pp_data->cap[214]->NotRange.Reserved4 = 127
+pp_data->cap[214]->NotButton.HasNull = 0
+pp_data->cap[214]->NotButton.Reserved4 = 0x000000
+pp_data->cap[214]->NotButton.LogicalMin = 0
+pp_data->cap[214]->NotButton.LogicalMax = 127
+pp_data->cap[214]->NotButton.PhysicalMin = 0
+pp_data->cap[214]->NotButton.PhysicalMax = 0
+pp_data->cap[214]->Units = 0
+pp_data->cap[214]->UnitsExp = 0
+
+pp_data->cap[215]->UsagePage = 0xFF01
+pp_data->cap[215]->ReportID = 0x81
+pp_data->cap[215]->BitPosition = 0
+pp_data->cap[215]->BitSize = 8
+pp_data->cap[215]->ReportCount = 1
+pp_data->cap[215]->BytePosition = 0x0006
+pp_data->cap[215]->BitCount = 8
+pp_data->cap[215]->BitField = 0x02
+pp_data->cap[215]->NextBytePosition = 0x0007
+pp_data->cap[215]->LinkCollection = 0x0004
+pp_data->cap[215]->LinkUsagePage = 0xFF01
+pp_data->cap[215]->LinkUsage = 0x0080
+pp_data->cap[215]->IsMultipleItemsForArray = 0
+pp_data->cap[215]->IsButtonCap = 0
+pp_data->cap[215]->IsPadding = 0
+pp_data->cap[215]->IsAbsolute = 1
+pp_data->cap[215]->IsRange = 0
+pp_data->cap[215]->IsAlias = 0
+pp_data->cap[215]->IsStringRange = 0
+pp_data->cap[215]->IsDesignatorRange = 0
+pp_data->cap[215]->Reserved1 = 0x000000
+pp_data->cap[215]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[215]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[215]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[215]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[215]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[215]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[215]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[215]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[215]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[215]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[215]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[215]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[215]->NotRange.Usage = 0x0081
+pp_data->cap[215]->NotRange.Reserved1 = 0x0081
+pp_data->cap[215]->NotRange.StringIndex = 0
+pp_data->cap[215]->NotRange.Reserved2 = 0
+pp_data->cap[215]->NotRange.DesignatorIndex = 0
+pp_data->cap[215]->NotRange.Reserved3 = 0
+pp_data->cap[215]->NotRange.DataIndex = 128
+pp_data->cap[215]->NotRange.Reserved4 = 128
+pp_data->cap[215]->NotButton.HasNull = 0
+pp_data->cap[215]->NotButton.Reserved4 = 0x000000
+pp_data->cap[215]->NotButton.LogicalMin = 0
+pp_data->cap[215]->NotButton.LogicalMax = 127
+pp_data->cap[215]->NotButton.PhysicalMin = 0
+pp_data->cap[215]->NotButton.PhysicalMax = 0
+pp_data->cap[215]->Units = 0
+pp_data->cap[215]->UnitsExp = 0
+
+pp_data->cap[216]->UsagePage = 0xFF01
+pp_data->cap[216]->ReportID = 0x81
+pp_data->cap[216]->BitPosition = 0
+pp_data->cap[216]->BitSize = 8
+pp_data->cap[216]->ReportCount = 1
+pp_data->cap[216]->BytePosition = 0x0005
+pp_data->cap[216]->BitCount = 8
+pp_data->cap[216]->BitField = 0x02
+pp_data->cap[216]->NextBytePosition = 0x0006
+pp_data->cap[216]->LinkCollection = 0x0004
+pp_data->cap[216]->LinkUsagePage = 0xFF01
+pp_data->cap[216]->LinkUsage = 0x0080
+pp_data->cap[216]->IsMultipleItemsForArray = 0
+pp_data->cap[216]->IsButtonCap = 0
+pp_data->cap[216]->IsPadding = 0
+pp_data->cap[216]->IsAbsolute = 1
+pp_data->cap[216]->IsRange = 0
+pp_data->cap[216]->IsAlias = 0
+pp_data->cap[216]->IsStringRange = 0
+pp_data->cap[216]->IsDesignatorRange = 0
+pp_data->cap[216]->Reserved1 = 0x000000
+pp_data->cap[216]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[216]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[216]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[216]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[216]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[216]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[216]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[216]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[216]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[216]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[216]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[216]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[216]->NotRange.Usage = 0x0081
+pp_data->cap[216]->NotRange.Reserved1 = 0x0081
+pp_data->cap[216]->NotRange.StringIndex = 0
+pp_data->cap[216]->NotRange.Reserved2 = 0
+pp_data->cap[216]->NotRange.DesignatorIndex = 0
+pp_data->cap[216]->NotRange.Reserved3 = 0
+pp_data->cap[216]->NotRange.DataIndex = 129
+pp_data->cap[216]->NotRange.Reserved4 = 129
+pp_data->cap[216]->NotButton.HasNull = 0
+pp_data->cap[216]->NotButton.Reserved4 = 0x000000
+pp_data->cap[216]->NotButton.LogicalMin = 0
+pp_data->cap[216]->NotButton.LogicalMax = 127
+pp_data->cap[216]->NotButton.PhysicalMin = 0
+pp_data->cap[216]->NotButton.PhysicalMax = 0
+pp_data->cap[216]->Units = 0
+pp_data->cap[216]->UnitsExp = 0
+
+pp_data->cap[217]->UsagePage = 0xFF01
+pp_data->cap[217]->ReportID = 0x81
+pp_data->cap[217]->BitPosition = 0
+pp_data->cap[217]->BitSize = 8
+pp_data->cap[217]->ReportCount = 1
+pp_data->cap[217]->BytePosition = 0x0004
+pp_data->cap[217]->BitCount = 8
+pp_data->cap[217]->BitField = 0x02
+pp_data->cap[217]->NextBytePosition = 0x0005
+pp_data->cap[217]->LinkCollection = 0x0004
+pp_data->cap[217]->LinkUsagePage = 0xFF01
+pp_data->cap[217]->LinkUsage = 0x0080
+pp_data->cap[217]->IsMultipleItemsForArray = 0
+pp_data->cap[217]->IsButtonCap = 0
+pp_data->cap[217]->IsPadding = 0
+pp_data->cap[217]->IsAbsolute = 1
+pp_data->cap[217]->IsRange = 0
+pp_data->cap[217]->IsAlias = 0
+pp_data->cap[217]->IsStringRange = 0
+pp_data->cap[217]->IsDesignatorRange = 0
+pp_data->cap[217]->Reserved1 = 0x000000
+pp_data->cap[217]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[217]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[217]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[217]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[217]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[217]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[217]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[217]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[217]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[217]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[217]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[217]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[217]->NotRange.Usage = 0x0081
+pp_data->cap[217]->NotRange.Reserved1 = 0x0081
+pp_data->cap[217]->NotRange.StringIndex = 0
+pp_data->cap[217]->NotRange.Reserved2 = 0
+pp_data->cap[217]->NotRange.DesignatorIndex = 0
+pp_data->cap[217]->NotRange.Reserved3 = 0
+pp_data->cap[217]->NotRange.DataIndex = 130
+pp_data->cap[217]->NotRange.Reserved4 = 130
+pp_data->cap[217]->NotButton.HasNull = 0
+pp_data->cap[217]->NotButton.Reserved4 = 0x000000
+pp_data->cap[217]->NotButton.LogicalMin = 0
+pp_data->cap[217]->NotButton.LogicalMax = 127
+pp_data->cap[217]->NotButton.PhysicalMin = 0
+pp_data->cap[217]->NotButton.PhysicalMax = 0
+pp_data->cap[217]->Units = 0
+pp_data->cap[217]->UnitsExp = 0
+
+pp_data->cap[218]->UsagePage = 0xFF01
+pp_data->cap[218]->ReportID = 0x81
+pp_data->cap[218]->BitPosition = 0
+pp_data->cap[218]->BitSize = 8
+pp_data->cap[218]->ReportCount = 1
+pp_data->cap[218]->BytePosition = 0x0003
+pp_data->cap[218]->BitCount = 8
+pp_data->cap[218]->BitField = 0x02
+pp_data->cap[218]->NextBytePosition = 0x0004
+pp_data->cap[218]->LinkCollection = 0x0004
+pp_data->cap[218]->LinkUsagePage = 0xFF01
+pp_data->cap[218]->LinkUsage = 0x0080
+pp_data->cap[218]->IsMultipleItemsForArray = 0
+pp_data->cap[218]->IsButtonCap = 0
+pp_data->cap[218]->IsPadding = 0
+pp_data->cap[218]->IsAbsolute = 1
+pp_data->cap[218]->IsRange = 0
+pp_data->cap[218]->IsAlias = 0
+pp_data->cap[218]->IsStringRange = 0
+pp_data->cap[218]->IsDesignatorRange = 0
+pp_data->cap[218]->Reserved1 = 0x000000
+pp_data->cap[218]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[218]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[218]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[218]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[218]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[218]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[218]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[218]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[218]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[218]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[218]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[218]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[218]->NotRange.Usage = 0x0081
+pp_data->cap[218]->NotRange.Reserved1 = 0x0081
+pp_data->cap[218]->NotRange.StringIndex = 0
+pp_data->cap[218]->NotRange.Reserved2 = 0
+pp_data->cap[218]->NotRange.DesignatorIndex = 0
+pp_data->cap[218]->NotRange.Reserved3 = 0
+pp_data->cap[218]->NotRange.DataIndex = 131
+pp_data->cap[218]->NotRange.Reserved4 = 131
+pp_data->cap[218]->NotButton.HasNull = 0
+pp_data->cap[218]->NotButton.Reserved4 = 0x000000
+pp_data->cap[218]->NotButton.LogicalMin = 0
+pp_data->cap[218]->NotButton.LogicalMax = 127
+pp_data->cap[218]->NotButton.PhysicalMin = 0
+pp_data->cap[218]->NotButton.PhysicalMax = 0
+pp_data->cap[218]->Units = 0
+pp_data->cap[218]->UnitsExp = 0
+
+pp_data->cap[219]->UsagePage = 0xFF01
+pp_data->cap[219]->ReportID = 0x81
+pp_data->cap[219]->BitPosition = 0
+pp_data->cap[219]->BitSize = 8
+pp_data->cap[219]->ReportCount = 1
+pp_data->cap[219]->BytePosition = 0x0002
+pp_data->cap[219]->BitCount = 8
+pp_data->cap[219]->BitField = 0x02
+pp_data->cap[219]->NextBytePosition = 0x0003
+pp_data->cap[219]->LinkCollection = 0x0004
+pp_data->cap[219]->LinkUsagePage = 0xFF01
+pp_data->cap[219]->LinkUsage = 0x0080
+pp_data->cap[219]->IsMultipleItemsForArray = 0
+pp_data->cap[219]->IsButtonCap = 0
+pp_data->cap[219]->IsPadding = 0
+pp_data->cap[219]->IsAbsolute = 1
+pp_data->cap[219]->IsRange = 0
+pp_data->cap[219]->IsAlias = 0
+pp_data->cap[219]->IsStringRange = 0
+pp_data->cap[219]->IsDesignatorRange = 0
+pp_data->cap[219]->Reserved1 = 0x000000
+pp_data->cap[219]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[219]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[219]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[219]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[219]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[219]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[219]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[219]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[219]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[219]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[219]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[219]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[219]->NotRange.Usage = 0x0081
+pp_data->cap[219]->NotRange.Reserved1 = 0x0081
+pp_data->cap[219]->NotRange.StringIndex = 0
+pp_data->cap[219]->NotRange.Reserved2 = 0
+pp_data->cap[219]->NotRange.DesignatorIndex = 0
+pp_data->cap[219]->NotRange.Reserved3 = 0
+pp_data->cap[219]->NotRange.DataIndex = 132
+pp_data->cap[219]->NotRange.Reserved4 = 132
+pp_data->cap[219]->NotButton.HasNull = 0
+pp_data->cap[219]->NotButton.Reserved4 = 0x000000
+pp_data->cap[219]->NotButton.LogicalMin = 0
+pp_data->cap[219]->NotButton.LogicalMax = 127
+pp_data->cap[219]->NotButton.PhysicalMin = 0
+pp_data->cap[219]->NotButton.PhysicalMax = 0
+pp_data->cap[219]->Units = 0
+pp_data->cap[219]->UnitsExp = 0
+
+pp_data->cap[220]->UsagePage = 0xFF01
+pp_data->cap[220]->ReportID = 0x81
+pp_data->cap[220]->BitPosition = 0
+pp_data->cap[220]->BitSize = 8
+pp_data->cap[220]->ReportCount = 1
+pp_data->cap[220]->BytePosition = 0x0001
+pp_data->cap[220]->BitCount = 8
+pp_data->cap[220]->BitField = 0x02
+pp_data->cap[220]->NextBytePosition = 0x0002
+pp_data->cap[220]->LinkCollection = 0x0004
+pp_data->cap[220]->LinkUsagePage = 0xFF01
+pp_data->cap[220]->LinkUsage = 0x0080
+pp_data->cap[220]->IsMultipleItemsForArray = 0
+pp_data->cap[220]->IsButtonCap = 0
+pp_data->cap[220]->IsPadding = 0
+pp_data->cap[220]->IsAbsolute = 1
+pp_data->cap[220]->IsRange = 0
+pp_data->cap[220]->IsAlias = 0
+pp_data->cap[220]->IsStringRange = 0
+pp_data->cap[220]->IsDesignatorRange = 0
+pp_data->cap[220]->Reserved1 = 0x000000
+pp_data->cap[220]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[220]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[220]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[220]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[220]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[220]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[220]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[220]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[220]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[220]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[220]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[220]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[220]->NotRange.Usage = 0x0081
+pp_data->cap[220]->NotRange.Reserved1 = 0x0081
+pp_data->cap[220]->NotRange.StringIndex = 0
+pp_data->cap[220]->NotRange.Reserved2 = 0
+pp_data->cap[220]->NotRange.DesignatorIndex = 0
+pp_data->cap[220]->NotRange.Reserved3 = 0
+pp_data->cap[220]->NotRange.DataIndex = 133
+pp_data->cap[220]->NotRange.Reserved4 = 133
+pp_data->cap[220]->NotButton.HasNull = 0
+pp_data->cap[220]->NotButton.Reserved4 = 0x000000
+pp_data->cap[220]->NotButton.LogicalMin = 0
+pp_data->cap[220]->NotButton.LogicalMax = 127
+pp_data->cap[220]->NotButton.PhysicalMin = 0
+pp_data->cap[220]->NotButton.PhysicalMax = 0
+pp_data->cap[220]->Units = 0
+pp_data->cap[220]->UnitsExp = 0
+
+# Feature hid_pp_cap struct:
+pp_data->cap[221]->UsagePage = 0xFF01
+pp_data->cap[221]->ReportID = 0xD0
+pp_data->cap[221]->BitPosition = 0
+pp_data->cap[221]->BitSize = 8
+pp_data->cap[221]->ReportCount = 32
+pp_data->cap[221]->BytePosition = 0x0001
+pp_data->cap[221]->BitCount = 256
+pp_data->cap[221]->BitField = 0x82
+pp_data->cap[221]->NextBytePosition = 0x0021
+pp_data->cap[221]->LinkCollection = 0x0005
+pp_data->cap[221]->LinkUsagePage = 0xFF01
+pp_data->cap[221]->LinkUsage = 0x00D0
+pp_data->cap[221]->IsMultipleItemsForArray = 0
+pp_data->cap[221]->IsButtonCap = 0
+pp_data->cap[221]->IsPadding = 0
+pp_data->cap[221]->IsAbsolute = 1
+pp_data->cap[221]->IsRange = 0
+pp_data->cap[221]->IsAlias = 0
+pp_data->cap[221]->IsStringRange = 0
+pp_data->cap[221]->IsDesignatorRange = 0
+pp_data->cap[221]->Reserved1 = 0x000000
+pp_data->cap[221]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[221]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[221]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[221]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[221]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[221]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[221]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[221]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[221]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[221]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[221]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[221]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[221]->NotRange.Usage = 0x00D1
+pp_data->cap[221]->NotRange.Reserved1 = 0x00D1
+pp_data->cap[221]->NotRange.StringIndex = 0
+pp_data->cap[221]->NotRange.Reserved2 = 0
+pp_data->cap[221]->NotRange.DesignatorIndex = 0
+pp_data->cap[221]->NotRange.Reserved3 = 0
+pp_data->cap[221]->NotRange.DataIndex = 0
+pp_data->cap[221]->NotRange.Reserved4 = 0
+pp_data->cap[221]->NotButton.HasNull = 0
+pp_data->cap[221]->NotButton.Reserved4 = 0x000000
+pp_data->cap[221]->NotButton.LogicalMin = 0
+pp_data->cap[221]->NotButton.LogicalMax = 255
+pp_data->cap[221]->NotButton.PhysicalMin = 0
+pp_data->cap[221]->NotButton.PhysicalMax = 0
+pp_data->cap[221]->Units = 0
+pp_data->cap[221]->UnitsExp = 0
+
+pp_data->cap[222]->UsagePage = 0xFF01
+pp_data->cap[222]->ReportID = 0xD1
+pp_data->cap[222]->BitPosition = 0
+pp_data->cap[222]->BitSize = 8
+pp_data->cap[222]->ReportCount = 32
+pp_data->cap[222]->BytePosition = 0x0001
+pp_data->cap[222]->BitCount = 256
+pp_data->cap[222]->BitField = 0x82
+pp_data->cap[222]->NextBytePosition = 0x0021
+pp_data->cap[222]->LinkCollection = 0x0006
+pp_data->cap[222]->LinkUsagePage = 0xFF01
+pp_data->cap[222]->LinkUsage = 0x00D0
+pp_data->cap[222]->IsMultipleItemsForArray = 0
+pp_data->cap[222]->IsButtonCap = 0
+pp_data->cap[222]->IsPadding = 0
+pp_data->cap[222]->IsAbsolute = 1
+pp_data->cap[222]->IsRange = 0
+pp_data->cap[222]->IsAlias = 0
+pp_data->cap[222]->IsStringRange = 0
+pp_data->cap[222]->IsDesignatorRange = 0
+pp_data->cap[222]->Reserved1 = 0x000000
+pp_data->cap[222]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[222]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[222]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[222]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[222]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[222]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[222]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[222]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[222]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[222]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[222]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[222]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[222]->NotRange.Usage = 0x00D1
+pp_data->cap[222]->NotRange.Reserved1 = 0x00D1
+pp_data->cap[222]->NotRange.StringIndex = 0
+pp_data->cap[222]->NotRange.Reserved2 = 0
+pp_data->cap[222]->NotRange.DesignatorIndex = 0
+pp_data->cap[222]->NotRange.Reserved3 = 0
+pp_data->cap[222]->NotRange.DataIndex = 1
+pp_data->cap[222]->NotRange.Reserved4 = 1
+pp_data->cap[222]->NotButton.HasNull = 0
+pp_data->cap[222]->NotButton.Reserved4 = 0x000000
+pp_data->cap[222]->NotButton.LogicalMin = 0
+pp_data->cap[222]->NotButton.LogicalMax = 255
+pp_data->cap[222]->NotButton.PhysicalMin = 0
+pp_data->cap[222]->NotButton.PhysicalMax = 0
+pp_data->cap[222]->Units = 0
+pp_data->cap[222]->UnitsExp = 0
+
+pp_data->cap[223]->UsagePage = 0xFF01
+pp_data->cap[223]->ReportID = 0xD2
+pp_data->cap[223]->BitPosition = 0
+pp_data->cap[223]->BitSize = 8
+pp_data->cap[223]->ReportCount = 32
+pp_data->cap[223]->BytePosition = 0x0001
+pp_data->cap[223]->BitCount = 256
+pp_data->cap[223]->BitField = 0x82
+pp_data->cap[223]->NextBytePosition = 0x0021
+pp_data->cap[223]->LinkCollection = 0x0007
+pp_data->cap[223]->LinkUsagePage = 0xFF01
+pp_data->cap[223]->LinkUsage = 0x00D0
+pp_data->cap[223]->IsMultipleItemsForArray = 0
+pp_data->cap[223]->IsButtonCap = 0
+pp_data->cap[223]->IsPadding = 0
+pp_data->cap[223]->IsAbsolute = 1
+pp_data->cap[223]->IsRange = 0
+pp_data->cap[223]->IsAlias = 0
+pp_data->cap[223]->IsStringRange = 0
+pp_data->cap[223]->IsDesignatorRange = 0
+pp_data->cap[223]->Reserved1 = 0x000000
+pp_data->cap[223]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[223]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[223]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[223]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[223]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[223]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[223]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[223]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[223]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[223]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[223]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[223]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[223]->NotRange.Usage = 0x00D1
+pp_data->cap[223]->NotRange.Reserved1 = 0x00D1
+pp_data->cap[223]->NotRange.StringIndex = 0
+pp_data->cap[223]->NotRange.Reserved2 = 0
+pp_data->cap[223]->NotRange.DesignatorIndex = 0
+pp_data->cap[223]->NotRange.Reserved3 = 0
+pp_data->cap[223]->NotRange.DataIndex = 2
+pp_data->cap[223]->NotRange.Reserved4 = 2
+pp_data->cap[223]->NotButton.HasNull = 0
+pp_data->cap[223]->NotButton.Reserved4 = 0x000000
+pp_data->cap[223]->NotButton.LogicalMin = 0
+pp_data->cap[223]->NotButton.LogicalMax = 255
+pp_data->cap[223]->NotButton.PhysicalMin = 0
+pp_data->cap[223]->NotButton.PhysicalMax = 0
+pp_data->cap[223]->Units = 0
+pp_data->cap[223]->UnitsExp = 0
+
+pp_data->cap[224]->UsagePage = 0xFF01
+pp_data->cap[224]->ReportID = 0xD3
+pp_data->cap[224]->BitPosition = 0
+pp_data->cap[224]->BitSize = 8
+pp_data->cap[224]->ReportCount = 32
+pp_data->cap[224]->BytePosition = 0x0001
+pp_data->cap[224]->BitCount = 256
+pp_data->cap[224]->BitField = 0x82
+pp_data->cap[224]->NextBytePosition = 0x0021
+pp_data->cap[224]->LinkCollection = 0x0008
+pp_data->cap[224]->LinkUsagePage = 0xFF01
+pp_data->cap[224]->LinkUsage = 0x00D0
+pp_data->cap[224]->IsMultipleItemsForArray = 0
+pp_data->cap[224]->IsButtonCap = 0
+pp_data->cap[224]->IsPadding = 0
+pp_data->cap[224]->IsAbsolute = 1
+pp_data->cap[224]->IsRange = 0
+pp_data->cap[224]->IsAlias = 0
+pp_data->cap[224]->IsStringRange = 0
+pp_data->cap[224]->IsDesignatorRange = 0
+pp_data->cap[224]->Reserved1 = 0x000000
+pp_data->cap[224]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[224]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[224]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[224]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[224]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[224]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[224]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[224]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[224]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[224]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[224]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[224]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[224]->NotRange.Usage = 0x00D1
+pp_data->cap[224]->NotRange.Reserved1 = 0x00D1
+pp_data->cap[224]->NotRange.StringIndex = 0
+pp_data->cap[224]->NotRange.Reserved2 = 0
+pp_data->cap[224]->NotRange.DesignatorIndex = 0
+pp_data->cap[224]->NotRange.Reserved3 = 0
+pp_data->cap[224]->NotRange.DataIndex = 3
+pp_data->cap[224]->NotRange.Reserved4 = 3
+pp_data->cap[224]->NotButton.HasNull = 0
+pp_data->cap[224]->NotButton.Reserved4 = 0x000000
+pp_data->cap[224]->NotButton.LogicalMin = 0
+pp_data->cap[224]->NotButton.LogicalMax = 255
+pp_data->cap[224]->NotButton.PhysicalMin = 0
+pp_data->cap[224]->NotButton.PhysicalMax = 0
+pp_data->cap[224]->Units = 0
+pp_data->cap[224]->UnitsExp = 0
+
+pp_data->cap[225]->UsagePage = 0xFF01
+pp_data->cap[225]->ReportID = 0xD4
+pp_data->cap[225]->BitPosition = 0
+pp_data->cap[225]->BitSize = 8
+pp_data->cap[225]->ReportCount = 32
+pp_data->cap[225]->BytePosition = 0x0001
+pp_data->cap[225]->BitCount = 256
+pp_data->cap[225]->BitField = 0x82
+pp_data->cap[225]->NextBytePosition = 0x0021
+pp_data->cap[225]->LinkCollection = 0x0009
+pp_data->cap[225]->LinkUsagePage = 0xFF01
+pp_data->cap[225]->LinkUsage = 0x00D0
+pp_data->cap[225]->IsMultipleItemsForArray = 0
+pp_data->cap[225]->IsButtonCap = 0
+pp_data->cap[225]->IsPadding = 0
+pp_data->cap[225]->IsAbsolute = 1
+pp_data->cap[225]->IsRange = 0
+pp_data->cap[225]->IsAlias = 0
+pp_data->cap[225]->IsStringRange = 0
+pp_data->cap[225]->IsDesignatorRange = 0
+pp_data->cap[225]->Reserved1 = 0x000000
+pp_data->cap[225]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[225]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[225]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[225]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[225]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[225]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[225]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[225]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[225]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[225]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[225]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[225]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[225]->NotRange.Usage = 0x00D1
+pp_data->cap[225]->NotRange.Reserved1 = 0x00D1
+pp_data->cap[225]->NotRange.StringIndex = 0
+pp_data->cap[225]->NotRange.Reserved2 = 0
+pp_data->cap[225]->NotRange.DesignatorIndex = 0
+pp_data->cap[225]->NotRange.Reserved3 = 0
+pp_data->cap[225]->NotRange.DataIndex = 4
+pp_data->cap[225]->NotRange.Reserved4 = 4
+pp_data->cap[225]->NotButton.HasNull = 0
+pp_data->cap[225]->NotButton.Reserved4 = 0x000000
+pp_data->cap[225]->NotButton.LogicalMin = 0
+pp_data->cap[225]->NotButton.LogicalMax = 255
+pp_data->cap[225]->NotButton.PhysicalMin = 0
+pp_data->cap[225]->NotButton.PhysicalMax = 0
+pp_data->cap[225]->Units = 0
+pp_data->cap[225]->UnitsExp = 0
+
+pp_data->cap[226]->UsagePage = 0xFF01
+pp_data->cap[226]->ReportID = 0xD5
+pp_data->cap[226]->BitPosition = 0
+pp_data->cap[226]->BitSize = 8
+pp_data->cap[226]->ReportCount = 32
+pp_data->cap[226]->BytePosition = 0x0001
+pp_data->cap[226]->BitCount = 256
+pp_data->cap[226]->BitField = 0x82
+pp_data->cap[226]->NextBytePosition = 0x0021
+pp_data->cap[226]->LinkCollection = 0x000A
+pp_data->cap[226]->LinkUsagePage = 0xFF01
+pp_data->cap[226]->LinkUsage = 0x00D0
+pp_data->cap[226]->IsMultipleItemsForArray = 0
+pp_data->cap[226]->IsButtonCap = 0
+pp_data->cap[226]->IsPadding = 0
+pp_data->cap[226]->IsAbsolute = 1
+pp_data->cap[226]->IsRange = 0
+pp_data->cap[226]->IsAlias = 0
+pp_data->cap[226]->IsStringRange = 0
+pp_data->cap[226]->IsDesignatorRange = 0
+pp_data->cap[226]->Reserved1 = 0x000000
+pp_data->cap[226]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[226]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[226]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[226]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[226]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[226]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[226]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[226]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[226]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[226]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[226]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[226]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[226]->NotRange.Usage = 0x00D1
+pp_data->cap[226]->NotRange.Reserved1 = 0x00D1
+pp_data->cap[226]->NotRange.StringIndex = 0
+pp_data->cap[226]->NotRange.Reserved2 = 0
+pp_data->cap[226]->NotRange.DesignatorIndex = 0
+pp_data->cap[226]->NotRange.Reserved3 = 0
+pp_data->cap[226]->NotRange.DataIndex = 5
+pp_data->cap[226]->NotRange.Reserved4 = 5
+pp_data->cap[226]->NotButton.HasNull = 0
+pp_data->cap[226]->NotButton.Reserved4 = 0x000000
+pp_data->cap[226]->NotButton.LogicalMin = 0
+pp_data->cap[226]->NotButton.LogicalMax = 255
+pp_data->cap[226]->NotButton.PhysicalMin = 0
+pp_data->cap[226]->NotButton.PhysicalMax = 0
+pp_data->cap[226]->Units = 0
+pp_data->cap[226]->UnitsExp = 0
+
+pp_data->cap[227]->UsagePage = 0xFF01
+pp_data->cap[227]->ReportID = 0xD6
+pp_data->cap[227]->BitPosition = 0
+pp_data->cap[227]->BitSize = 8
+pp_data->cap[227]->ReportCount = 32
+pp_data->cap[227]->BytePosition = 0x0001
+pp_data->cap[227]->BitCount = 256
+pp_data->cap[227]->BitField = 0x82
+pp_data->cap[227]->NextBytePosition = 0x0021
+pp_data->cap[227]->LinkCollection = 0x000B
+pp_data->cap[227]->LinkUsagePage = 0xFF01
+pp_data->cap[227]->LinkUsage = 0x00D0
+pp_data->cap[227]->IsMultipleItemsForArray = 0
+pp_data->cap[227]->IsButtonCap = 0
+pp_data->cap[227]->IsPadding = 0
+pp_data->cap[227]->IsAbsolute = 1
+pp_data->cap[227]->IsRange = 0
+pp_data->cap[227]->IsAlias = 0
+pp_data->cap[227]->IsStringRange = 0
+pp_data->cap[227]->IsDesignatorRange = 0
+pp_data->cap[227]->Reserved1 = 0x000000
+pp_data->cap[227]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[227]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[227]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[227]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[227]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[227]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[227]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[227]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[227]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[227]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[227]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[227]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[227]->NotRange.Usage = 0x00D1
+pp_data->cap[227]->NotRange.Reserved1 = 0x00D1
+pp_data->cap[227]->NotRange.StringIndex = 0
+pp_data->cap[227]->NotRange.Reserved2 = 0
+pp_data->cap[227]->NotRange.DesignatorIndex = 0
+pp_data->cap[227]->NotRange.Reserved3 = 0
+pp_data->cap[227]->NotRange.DataIndex = 6
+pp_data->cap[227]->NotRange.Reserved4 = 6
+pp_data->cap[227]->NotButton.HasNull = 0
+pp_data->cap[227]->NotButton.Reserved4 = 0x000000
+pp_data->cap[227]->NotButton.LogicalMin = 0
+pp_data->cap[227]->NotButton.LogicalMax = 255
+pp_data->cap[227]->NotButton.PhysicalMin = 0
+pp_data->cap[227]->NotButton.PhysicalMax = 0
+pp_data->cap[227]->Units = 0
+pp_data->cap[227]->UnitsExp = 0
+
+pp_data->cap[228]->UsagePage = 0xFF01
+pp_data->cap[228]->ReportID = 0xD8
+pp_data->cap[228]->BitPosition = 0
+pp_data->cap[228]->BitSize = 8
+pp_data->cap[228]->ReportCount = 32
+pp_data->cap[228]->BytePosition = 0x0001
+pp_data->cap[228]->BitCount = 256
+pp_data->cap[228]->BitField = 0x82
+pp_data->cap[228]->NextBytePosition = 0x0021
+pp_data->cap[228]->LinkCollection = 0x000C
+pp_data->cap[228]->LinkUsagePage = 0xFF01
+pp_data->cap[228]->LinkUsage = 0x00D0
+pp_data->cap[228]->IsMultipleItemsForArray = 0
+pp_data->cap[228]->IsButtonCap = 0
+pp_data->cap[228]->IsPadding = 0
+pp_data->cap[228]->IsAbsolute = 1
+pp_data->cap[228]->IsRange = 0
+pp_data->cap[228]->IsAlias = 0
+pp_data->cap[228]->IsStringRange = 0
+pp_data->cap[228]->IsDesignatorRange = 0
+pp_data->cap[228]->Reserved1 = 0x000000
+pp_data->cap[228]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[228]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[228]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[228]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[228]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[228]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[228]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[228]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[228]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[228]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[228]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[228]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[228]->NotRange.Usage = 0x00D1
+pp_data->cap[228]->NotRange.Reserved1 = 0x00D1
+pp_data->cap[228]->NotRange.StringIndex = 0
+pp_data->cap[228]->NotRange.Reserved2 = 0
+pp_data->cap[228]->NotRange.DesignatorIndex = 0
+pp_data->cap[228]->NotRange.Reserved3 = 0
+pp_data->cap[228]->NotRange.DataIndex = 7
+pp_data->cap[228]->NotRange.Reserved4 = 7
+pp_data->cap[228]->NotButton.HasNull = 0
+pp_data->cap[228]->NotButton.Reserved4 = 0x000000
+pp_data->cap[228]->NotButton.LogicalMin = 0
+pp_data->cap[228]->NotButton.LogicalMax = 255
+pp_data->cap[228]->NotButton.PhysicalMin = 0
+pp_data->cap[228]->NotButton.PhysicalMax = 0
+pp_data->cap[228]->Units = 0
+pp_data->cap[228]->UnitsExp = 0
+
+pp_data->cap[229]->UsagePage = 0xFF01
+pp_data->cap[229]->ReportID = 0xD9
+pp_data->cap[229]->BitPosition = 0
+pp_data->cap[229]->BitSize = 8
+pp_data->cap[229]->ReportCount = 32
+pp_data->cap[229]->BytePosition = 0x0001
+pp_data->cap[229]->BitCount = 256
+pp_data->cap[229]->BitField = 0x82
+pp_data->cap[229]->NextBytePosition = 0x0021
+pp_data->cap[229]->LinkCollection = 0x000D
+pp_data->cap[229]->LinkUsagePage = 0xFF01
+pp_data->cap[229]->LinkUsage = 0x00D0
+pp_data->cap[229]->IsMultipleItemsForArray = 0
+pp_data->cap[229]->IsButtonCap = 0
+pp_data->cap[229]->IsPadding = 0
+pp_data->cap[229]->IsAbsolute = 1
+pp_data->cap[229]->IsRange = 0
+pp_data->cap[229]->IsAlias = 0
+pp_data->cap[229]->IsStringRange = 0
+pp_data->cap[229]->IsDesignatorRange = 0
+pp_data->cap[229]->Reserved1 = 0x000000
+pp_data->cap[229]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[229]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[229]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[229]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[229]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[229]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[229]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[229]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[229]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[229]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[229]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[229]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[229]->NotRange.Usage = 0x00D1
+pp_data->cap[229]->NotRange.Reserved1 = 0x00D1
+pp_data->cap[229]->NotRange.StringIndex = 0
+pp_data->cap[229]->NotRange.Reserved2 = 0
+pp_data->cap[229]->NotRange.DesignatorIndex = 0
+pp_data->cap[229]->NotRange.Reserved3 = 0
+pp_data->cap[229]->NotRange.DataIndex = 8
+pp_data->cap[229]->NotRange.Reserved4 = 8
+pp_data->cap[229]->NotButton.HasNull = 0
+pp_data->cap[229]->NotButton.Reserved4 = 0x000000
+pp_data->cap[229]->NotButton.LogicalMin = 0
+pp_data->cap[229]->NotButton.LogicalMax = 255
+pp_data->cap[229]->NotButton.PhysicalMin = 0
+pp_data->cap[229]->NotButton.PhysicalMax = 0
+pp_data->cap[229]->Units = 0
+pp_data->cap[229]->UnitsExp = 0
+
+pp_data->cap[230]->UsagePage = 0xFF01
+pp_data->cap[230]->ReportID = 0xF1
+pp_data->cap[230]->BitPosition = 0
+pp_data->cap[230]->BitSize = 8
+pp_data->cap[230]->ReportCount = 2
+pp_data->cap[230]->BytePosition = 0x0001
+pp_data->cap[230]->BitCount = 16
+pp_data->cap[230]->BitField = 0x82
+pp_data->cap[230]->NextBytePosition = 0x0003
+pp_data->cap[230]->LinkCollection = 0x000E
+pp_data->cap[230]->LinkUsagePage = 0xFF01
+pp_data->cap[230]->LinkUsage = 0x00D0
+pp_data->cap[230]->IsMultipleItemsForArray = 0
+pp_data->cap[230]->IsButtonCap = 0
+pp_data->cap[230]->IsPadding = 0
+pp_data->cap[230]->IsAbsolute = 1
+pp_data->cap[230]->IsRange = 0
+pp_data->cap[230]->IsAlias = 0
+pp_data->cap[230]->IsStringRange = 0
+pp_data->cap[230]->IsDesignatorRange = 0
+pp_data->cap[230]->Reserved1 = 0x000000
+pp_data->cap[230]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[230]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[230]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[230]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[230]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[230]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[230]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[230]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[230]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[230]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[230]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[230]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[230]->NotRange.Usage = 0x00D1
+pp_data->cap[230]->NotRange.Reserved1 = 0x00D1
+pp_data->cap[230]->NotRange.StringIndex = 0
+pp_data->cap[230]->NotRange.Reserved2 = 0
+pp_data->cap[230]->NotRange.DesignatorIndex = 0
+pp_data->cap[230]->NotRange.Reserved3 = 0
+pp_data->cap[230]->NotRange.DataIndex = 9
+pp_data->cap[230]->NotRange.Reserved4 = 9
+pp_data->cap[230]->NotButton.HasNull = 0
+pp_data->cap[230]->NotButton.Reserved4 = 0x000000
+pp_data->cap[230]->NotButton.LogicalMin = 0
+pp_data->cap[230]->NotButton.LogicalMax = 255
+pp_data->cap[230]->NotButton.PhysicalMin = 0
+pp_data->cap[230]->NotButton.PhysicalMax = 0
+pp_data->cap[230]->Units = 0
+pp_data->cap[230]->UnitsExp = 0
+
+pp_data->cap[231]->UsagePage = 0xFF01
+pp_data->cap[231]->ReportID = 0xF3
+pp_data->cap[231]->BitPosition = 0
+pp_data->cap[231]->BitSize = 8
+pp_data->cap[231]->ReportCount = 2
+pp_data->cap[231]->BytePosition = 0x0001
+pp_data->cap[231]->BitCount = 16
+pp_data->cap[231]->BitField = 0x82
+pp_data->cap[231]->NextBytePosition = 0x0003
+pp_data->cap[231]->LinkCollection = 0x000F
+pp_data->cap[231]->LinkUsagePage = 0xFF01
+pp_data->cap[231]->LinkUsage = 0x00D0
+pp_data->cap[231]->IsMultipleItemsForArray = 0
+pp_data->cap[231]->IsButtonCap = 0
+pp_data->cap[231]->IsPadding = 0
+pp_data->cap[231]->IsAbsolute = 1
+pp_data->cap[231]->IsRange = 0
+pp_data->cap[231]->IsAlias = 0
+pp_data->cap[231]->IsStringRange = 0
+pp_data->cap[231]->IsDesignatorRange = 0
+pp_data->cap[231]->Reserved1 = 0x000000
+pp_data->cap[231]->pp_cap->UnknownTokens[0].Token = 0x00
+pp_data->cap[231]->pp_cap->UnknownTokens[0].Reserved = 0x000000
+pp_data->cap[231]->pp_cap->UnknownTokens[0].BitField = 0x00000000
+pp_data->cap[231]->pp_cap->UnknownTokens[1].Token = 0x00
+pp_data->cap[231]->pp_cap->UnknownTokens[1].Reserved = 0x000000
+pp_data->cap[231]->pp_cap->UnknownTokens[1].BitField = 0x00000000
+pp_data->cap[231]->pp_cap->UnknownTokens[2].Token = 0x00
+pp_data->cap[231]->pp_cap->UnknownTokens[2].Reserved = 0x000000
+pp_data->cap[231]->pp_cap->UnknownTokens[2].BitField = 0x00000000
+pp_data->cap[231]->pp_cap->UnknownTokens[3].Token = 0x00
+pp_data->cap[231]->pp_cap->UnknownTokens[3].Reserved = 0x000000
+pp_data->cap[231]->pp_cap->UnknownTokens[3].BitField = 0x00000000
+pp_data->cap[231]->NotRange.Usage = 0x00D1
+pp_data->cap[231]->NotRange.Reserved1 = 0x00D1
+pp_data->cap[231]->NotRange.StringIndex = 0
+pp_data->cap[231]->NotRange.Reserved2 = 0
+pp_data->cap[231]->NotRange.DesignatorIndex = 0
+pp_data->cap[231]->NotRange.Reserved3 = 0
+pp_data->cap[231]->NotRange.DataIndex = 10
+pp_data->cap[231]->NotRange.Reserved4 = 10
+pp_data->cap[231]->NotButton.HasNull = 0
+pp_data->cap[231]->NotButton.Reserved4 = 0x000000
+pp_data->cap[231]->NotButton.LogicalMin = 0
+pp_data->cap[231]->NotButton.LogicalMax = 127
+pp_data->cap[231]->NotButton.PhysicalMin = 0
+pp_data->cap[231]->NotButton.PhysicalMax = 0
+pp_data->cap[231]->Units = 0
+pp_data->cap[231]->UnitsExp = 0
+
+# Link Collections:
+pp_data->LinkCollectionArray[0]->LinkUsage = 0x0000
+pp_data->LinkCollectionArray[0]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[0]->Parent = 0
+pp_data->LinkCollectionArray[0]->NumberOfChildren = 15
+pp_data->LinkCollectionArray[0]->NextSibling = 0
+pp_data->LinkCollectionArray[0]->FirstChild = 15
+pp_data->LinkCollectionArray[0]->CollectionType = 1
+pp_data->LinkCollectionArray[0]->IsAlias = 0
+pp_data->LinkCollectionArray[0]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[1]->LinkUsage = 0x0001
+pp_data->LinkCollectionArray[1]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[1]->Parent = 0
+pp_data->LinkCollectionArray[1]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[1]->NextSibling = 0
+pp_data->LinkCollectionArray[1]->FirstChild = 0
+pp_data->LinkCollectionArray[1]->CollectionType = 2
+pp_data->LinkCollectionArray[1]->IsAlias = 0
+pp_data->LinkCollectionArray[1]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[2]->LinkUsage = 0x0002
+pp_data->LinkCollectionArray[2]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[2]->Parent = 0
+pp_data->LinkCollectionArray[2]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[2]->NextSibling = 1
+pp_data->LinkCollectionArray[2]->FirstChild = 0
+pp_data->LinkCollectionArray[2]->CollectionType = 2
+pp_data->LinkCollectionArray[2]->IsAlias = 0
+pp_data->LinkCollectionArray[2]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[3]->LinkUsage = 0x0080
+pp_data->LinkCollectionArray[3]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[3]->Parent = 0
+pp_data->LinkCollectionArray[3]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[3]->NextSibling = 2
+pp_data->LinkCollectionArray[3]->FirstChild = 0
+pp_data->LinkCollectionArray[3]->CollectionType = 2
+pp_data->LinkCollectionArray[3]->IsAlias = 0
+pp_data->LinkCollectionArray[3]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[4]->LinkUsage = 0x0080
+pp_data->LinkCollectionArray[4]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[4]->Parent = 0
+pp_data->LinkCollectionArray[4]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[4]->NextSibling = 3
+pp_data->LinkCollectionArray[4]->FirstChild = 0
+pp_data->LinkCollectionArray[4]->CollectionType = 2
+pp_data->LinkCollectionArray[4]->IsAlias = 0
+pp_data->LinkCollectionArray[4]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[5]->LinkUsage = 0x00D0
+pp_data->LinkCollectionArray[5]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[5]->Parent = 0
+pp_data->LinkCollectionArray[5]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[5]->NextSibling = 4
+pp_data->LinkCollectionArray[5]->FirstChild = 0
+pp_data->LinkCollectionArray[5]->CollectionType = 2
+pp_data->LinkCollectionArray[5]->IsAlias = 0
+pp_data->LinkCollectionArray[5]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[6]->LinkUsage = 0x00D0
+pp_data->LinkCollectionArray[6]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[6]->Parent = 0
+pp_data->LinkCollectionArray[6]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[6]->NextSibling = 5
+pp_data->LinkCollectionArray[6]->FirstChild = 0
+pp_data->LinkCollectionArray[6]->CollectionType = 2
+pp_data->LinkCollectionArray[6]->IsAlias = 0
+pp_data->LinkCollectionArray[6]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[7]->LinkUsage = 0x00D0
+pp_data->LinkCollectionArray[7]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[7]->Parent = 0
+pp_data->LinkCollectionArray[7]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[7]->NextSibling = 6
+pp_data->LinkCollectionArray[7]->FirstChild = 0
+pp_data->LinkCollectionArray[7]->CollectionType = 2
+pp_data->LinkCollectionArray[7]->IsAlias = 0
+pp_data->LinkCollectionArray[7]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[8]->LinkUsage = 0x00D0
+pp_data->LinkCollectionArray[8]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[8]->Parent = 0
+pp_data->LinkCollectionArray[8]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[8]->NextSibling = 7
+pp_data->LinkCollectionArray[8]->FirstChild = 0
+pp_data->LinkCollectionArray[8]->CollectionType = 2
+pp_data->LinkCollectionArray[8]->IsAlias = 0
+pp_data->LinkCollectionArray[8]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[9]->LinkUsage = 0x00D0
+pp_data->LinkCollectionArray[9]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[9]->Parent = 0
+pp_data->LinkCollectionArray[9]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[9]->NextSibling = 8
+pp_data->LinkCollectionArray[9]->FirstChild = 0
+pp_data->LinkCollectionArray[9]->CollectionType = 2
+pp_data->LinkCollectionArray[9]->IsAlias = 0
+pp_data->LinkCollectionArray[9]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[10]->LinkUsage = 0x00D0
+pp_data->LinkCollectionArray[10]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[10]->Parent = 0
+pp_data->LinkCollectionArray[10]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[10]->NextSibling = 9
+pp_data->LinkCollectionArray[10]->FirstChild = 0
+pp_data->LinkCollectionArray[10]->CollectionType = 2
+pp_data->LinkCollectionArray[10]->IsAlias = 0
+pp_data->LinkCollectionArray[10]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[11]->LinkUsage = 0x00D0
+pp_data->LinkCollectionArray[11]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[11]->Parent = 0
+pp_data->LinkCollectionArray[11]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[11]->NextSibling = 10
+pp_data->LinkCollectionArray[11]->FirstChild = 0
+pp_data->LinkCollectionArray[11]->CollectionType = 2
+pp_data->LinkCollectionArray[11]->IsAlias = 0
+pp_data->LinkCollectionArray[11]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[12]->LinkUsage = 0x00D0
+pp_data->LinkCollectionArray[12]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[12]->Parent = 0
+pp_data->LinkCollectionArray[12]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[12]->NextSibling = 11
+pp_data->LinkCollectionArray[12]->FirstChild = 0
+pp_data->LinkCollectionArray[12]->CollectionType = 2
+pp_data->LinkCollectionArray[12]->IsAlias = 0
+pp_data->LinkCollectionArray[12]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[13]->LinkUsage = 0x00D0
+pp_data->LinkCollectionArray[13]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[13]->Parent = 0
+pp_data->LinkCollectionArray[13]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[13]->NextSibling = 12
+pp_data->LinkCollectionArray[13]->FirstChild = 0
+pp_data->LinkCollectionArray[13]->CollectionType = 2
+pp_data->LinkCollectionArray[13]->IsAlias = 0
+pp_data->LinkCollectionArray[13]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[14]->LinkUsage = 0x00D0
+pp_data->LinkCollectionArray[14]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[14]->Parent = 0
+pp_data->LinkCollectionArray[14]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[14]->NextSibling = 13
+pp_data->LinkCollectionArray[14]->FirstChild = 0
+pp_data->LinkCollectionArray[14]->CollectionType = 2
+pp_data->LinkCollectionArray[14]->IsAlias = 0
+pp_data->LinkCollectionArray[14]->Reserved = 0x00000000
+pp_data->LinkCollectionArray[15]->LinkUsage = 0x00D0
+pp_data->LinkCollectionArray[15]->LinkUsagePage = 0xFF01
+pp_data->LinkCollectionArray[15]->Parent = 0
+pp_data->LinkCollectionArray[15]->NumberOfChildren = 0
+pp_data->LinkCollectionArray[15]->NextSibling = 14
+pp_data->LinkCollectionArray[15]->FirstChild = 0
+pp_data->LinkCollectionArray[15]->CollectionType = 2
+pp_data->LinkCollectionArray[15]->IsAlias = 0
+pp_data->LinkCollectionArray[15]->Reserved = 0x00000000
diff --git a/src/hidapi/windows/test/data/17CC_1130_0000_FF01_expected.rpt_desc b/src/hidapi/windows/test/data/17CC_1130_0000_FF01_expected.rpt_desc
new file mode 100644
index 0000000..9bcc814
--- /dev/null
+++ b/src/hidapi/windows/test/data/17CC_1130_0000_FF01_expected.rpt_desc
@@ -0,0 +1,75 @@
+0x06, 0x01, 0xFF, 0x09, 0x00, 0xA1, 0x01, 0x09, 0x01, 0xA1,
+0x02, 0x85, 0x01, 0x09, 0x03, 0x09, 0x03, 0x09, 0x03, 0x09,
+0x03, 0x15, 0x00, 0x25, 0x0F, 0x75, 0x04, 0x95, 0x04, 0x81,
+0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09,
+0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09,
+0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09,
+0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09,
+0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09,
+0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09,
+0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09,
+0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09,
+0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09,
+0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x02, 0x09, 0x0B, 0x09,
+0x0B, 0x09, 0x0B, 0x09, 0x0B, 0x09, 0x0B, 0x09, 0x0B, 0x09,
+0x0B, 0x09, 0x0B, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95,
+0x38, 0x81, 0x02, 0xC0, 0x09, 0x02, 0xA1, 0x02, 0x85, 0x02,
+0x09, 0x04, 0x09, 0x04, 0x09, 0x04, 0x09, 0x04, 0x09, 0x04,
+0x09, 0x04, 0x09, 0x04, 0x09, 0x04, 0x09, 0x04, 0x09, 0x04,
+0x09, 0x04, 0x09, 0x04, 0x09, 0x04, 0x09, 0x04, 0x09, 0x04,
+0x09, 0x04, 0x09, 0x04, 0x09, 0x04, 0x09, 0x04, 0x09, 0x04,
+0x09, 0x04, 0x09, 0x04, 0x09, 0x04, 0x09, 0x04, 0x09, 0x04,
+0x09, 0x04, 0x15, 0x00, 0x26, 0xFF, 0x0F, 0x75, 0x10, 0x95,
+0x1A, 0x81, 0x02, 0xC0, 0x09, 0x80, 0xA1, 0x02, 0x85, 0x80,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81,
+0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x15, 0x00,
+0x25, 0x7F, 0x75, 0x08, 0x95, 0x5E, 0x91, 0x02, 0xC0, 0x09,
+0x80, 0xA1, 0x02, 0x85, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09,
+0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09,
+0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09,
+0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09,
+0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09,
+0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09,
+0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09,
+0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09, 0x81, 0x09,
+0x81, 0x09, 0x81, 0x09, 0x81, 0x15, 0x00, 0x25, 0x7F, 0x75,
+0x08, 0x95, 0x28, 0x91, 0x02, 0xC0, 0x09, 0xD0, 0xA1, 0x02,
+0x85, 0xD0, 0x09, 0xD1, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x20, 0xB1, 0x82, 0xC0, 0x09, 0xD0, 0xA1, 0x02,
+0x85, 0xD1, 0x09, 0xD1, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x20, 0xB1, 0x82, 0xC0, 0x09, 0xD0, 0xA1, 0x02,
+0x85, 0xD2, 0x09, 0xD1, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x20, 0xB1, 0x82, 0xC0, 0x09, 0xD0, 0xA1, 0x02,
+0x85, 0xD3, 0x09, 0xD1, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x20, 0xB1, 0x82, 0xC0, 0x09, 0xD0, 0xA1, 0x02,
+0x85, 0xD4, 0x09, 0xD1, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x20, 0xB1, 0x82, 0xC0, 0x09, 0xD0, 0xA1, 0x02,
+0x85, 0xD5, 0x09, 0xD1, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x20, 0xB1, 0x82, 0xC0, 0x09, 0xD0, 0xA1, 0x02,
+0x85, 0xD6, 0x09, 0xD1, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x20, 0xB1, 0x82, 0xC0, 0x09, 0xD0, 0xA1, 0x02,
+0x85, 0xD8, 0x09, 0xD1, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x20, 0xB1, 0x82, 0xC0, 0x09, 0xD0, 0xA1, 0x02,
+0x85, 0xD9, 0x09, 0xD1, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x20, 0xB1, 0x82, 0xC0, 0x09, 0xD0, 0xA1, 0x02,
+0x85, 0xF1, 0x09, 0xD1, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75,
+0x08, 0x95, 0x02, 0xB1, 0x82, 0xC0, 0x09, 0xD0, 0xA1, 0x02,
+0x85, 0xF3, 0x09, 0xD1, 0x15, 0x00, 0x25, 0x7F, 0x75, 0x08,
+0x95, 0x02, 0xB1, 0x82, 0xC0, 0xC0,
\ No newline at end of file
diff --git a/src/hidapi/windows/test/data/17CC_1130_0000_FF01_real.rpt_desc b/src/hidapi/windows/test/data/17CC_1130_0000_FF01_real.rpt_desc
new file mode 100644
index 0000000..7f908f8
--- /dev/null
+++ b/src/hidapi/windows/test/data/17CC_1130_0000_FF01_real.rpt_desc
@@ -0,0 +1,381 @@
+Usage Page (Vendor-Defined 2) 06 01 FF
+Usage (Undefined) 09 00
+Collection (Application) A1 01
+ Usage (Vendor-Defined 1) 09 01
+ Collection (Logical) A1 02
+ Report ID (1) 85 01
+ Usage (Vendor-Defined 3) 09 03
+ Usage (Vendor-Defined 3) 09 03
+ Usage (Vendor-Defined 3) 09 03
+ Usage (Vendor-Defined 3) 09 03
+ Logical Minimum (0) 15 00
+ Logical Maximum (15) 25 0F
+ Report Size (4) 75 04
+ Report Count (4) 95 04
+ Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Usage (Vendor-Defined 2) 09 02
+ Logical Minimum (0) 15 00
+ Logical Maximum (1) 25 01
+ Report Size (1) 75 01
+ Report Count (48) 95 30
+ Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
+ Usage (Vendor-Defined 11) 09 0B
+ Usage (Vendor-Defined 11) 09 0B
+ Usage (Vendor-Defined 11) 09 0B
+ Usage (Vendor-Defined 11) 09 0B
+ Usage (Vendor-Defined 11) 09 0B
+ Usage (Vendor-Defined 11) 09 0B
+ Usage (Vendor-Defined 11) 09 0B
+ Usage (Vendor-Defined 11) 09 0B
+ Logical Minimum (0) 15 00
+ Logical Maximum (1) 25 01
+ Report Size (1) 75 01
+ Report Count (8) 95 08
+ Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
+ End Collection C0
+ Usage (Vendor-Defined 2) 09 02
+ Collection (Logical) A1 02
+ Report ID (2) 85 02
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Usage (Vendor-Defined 4) 09 04
+ Logical Minimum (0) 15 00
+ Logical Maximum (4095) 26 FF 0F
+ Report Size (16) 75 10
+ Report Count (26) 95 1A
+ Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
+ End Collection C0
+ Usage (Vendor-Defined 128) 09 80
+ Collection (Logical) A1 02
+ Report ID (128) 85 80
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Logical Minimum (0) 15 00
+ Logical Maximum (127) 25 7F
+ Report Count (94) 95 5E
+ Report Size (8) 75 08
+ Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 02
+ End Collection C0
+ Usage (Vendor-Defined 128) 09 80
+ Collection (Logical) A1 02
+ Report ID (129) 85 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Usage (Vendor-Defined 129) 09 81
+ Logical Minimum (0) 15 00
+ Logical Maximum (127) 25 7F
+ Report Count (40) 95 28
+ Report Size (8) 75 08
+ Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 02
+ End Collection C0
+ Usage (Vendor-Defined 208) 09 D0
+ Collection (Logical) A1 02
+ Report ID (208) 85 D0
+ Usage (Vendor-Defined 209) 09 D1
+ Logical Minimum (0) 15 00
+ Logical Maximum (255) 26 FF 00
+ Report Size (8) 75 08
+ Report Count (32) 95 20
+ Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,Vol,Bit) B1 82
+ End Collection C0
+ Usage (Vendor-Defined 208) 09 D0
+ Collection (Logical) A1 02
+ Report ID (209) 85 D1
+ Usage (Vendor-Defined 209) 09 D1
+ Logical Minimum (0) 15 00
+ Logical Maximum (255) 26 FF 00
+ Report Size (8) 75 08
+ Report Count (32) 95 20
+ Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,Vol,Bit) B1 82
+ End Collection C0
+ Usage (Vendor-Defined 208) 09 D0
+ Collection (Logical) A1 02
+ Report ID (210) 85 D2
+ Usage (Vendor-Defined 209) 09 D1
+ Logical Minimum (0) 15 00
+ Logical Maximum (255) 26 FF 00
+ Report Size (8) 75 08
+ Report Count (32) 95 20
+ Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,Vol,Bit) B1 82
+ End Collection C0
+ Usage (Vendor-Defined 208) 09 D0
+ Collection (Logical) A1 02
+ Report ID (211) 85 D3
+ Usage (Vendor-Defined 209) 09 D1
+ Logical Minimum (0) 15 00
+ Logical Maximum (255) 26 FF 00
+ Report Size (8) 75 08
+ Report Count (32) 95 20
+ Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,Vol,Bit) B1 82
+ End Collection C0
+ Usage (Vendor-Defined 208) 09 D0
+ Collection (Logical) A1 02
+ Report ID (212) 85 D4
+ Usage (Vendor-Defined 209) 09 D1
+ Logical Minimum (0) 15 00
+ Logical Maximum (255) 26 FF 00
+ Report Size (8) 75 08
+ Report Count (32) 95 20
+ Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,Vol,Bit) B1 82
+ End Collection C0
+ Usage (Vendor-Defined 208) 09 D0
+ Collection (Logical) A1 02
+ Report ID (213) 85 D5
+ Usage (Vendor-Defined 209) 09 D1
+ Logical Minimum (0) 15 00
+ Logical Maximum (255) 26 FF 00
+ Report Size (8) 75 08
+ Report Count (32) 95 20
+ Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,Vol,Bit) B1 82
+ End Collection C0
+ Usage (Vendor-Defined 208) 09 D0
+ Collection (Logical) A1 02
+ Report ID (214) 85 D6
+ Usage (Vendor-Defined 209) 09 D1
+ Logical Minimum (0) 15 00
+ Logical Maximum (255) 26 FF 00
+ Report Size (8) 75 08
+ Report Count (32) 95 20
+ Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,Vol,Bit) B1 82
+ End Collection C0
+ Usage (Vendor-Defined 208) 09 D0
+ Collection (Logical) A1 02
+ Report ID (216) 85 D8
+ Usage (Vendor-Defined 209) 09 D1
+ Logical Minimum (0) 15 00
+ Logical Maximum (255) 26 FF 00
+ Report Size (8) 75 08
+ Report Count (32) 95 20
+ Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,Vol,Bit) B1 82
+ End Collection C0
+ Usage (Vendor-Defined 208) 09 D0
+ Collection (Logical) A1 02
+ Report ID (217) 85 D9
+ Usage (Vendor-Defined 209) 09 D1
+ Logical Minimum (0) 15 00
+ Logical Maximum (255) 26 FF 00
+ Report Size (8) 75 08
+ Report Count (32) 95 20
+ Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,Vol,Bit) B1 82
+ End Collection C0
+ Usage (Vendor-Defined 208) 09 D0
+ Collection (Logical) A1 02
+ Report ID (241) 85 F1
+ Usage (Vendor-Defined 209) 09 D1
+ Logical Minimum (0) 15 00
+ Logical Maximum (255) 26 FF 00
+ Report Size (8) 75 08
+ Report Count (2) 95 02
+ Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,Vol,Bit) B1 82
+ End Collection C0
+ Usage (Vendor-Defined 208) 09 D0
+ Collection (Logical) A1 02
+ Report ID (243) 85 F3
+ Usage (Vendor-Defined 209) 09 D1
+ Logical Minimum (0) 15 00
+ Logical Maximum (127) 25 7F
+ Report Size (8) 75 08
+ Report Count (2) 95 02
+ Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,Vol,Bit) B1 82
+ End Collection C0
+End Collection C0
diff --git a/src/hidapi/windows/test/hid_report_reconstructor_test.c b/src/hidapi/windows/test/hid_report_reconstructor_test.c
new file mode 100644
index 0000000..bcd7cd4
--- /dev/null
+++ b/src/hidapi/windows/test/hid_report_reconstructor_test.c
@@ -0,0 +1,561 @@
+#include "../hidapi_descriptor_reconstruct.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(__MINGW32__)
+#pragma GCC diagnostic ignored "-Wformat"
+#endif
+static hidp_preparsed_data * alloc_preparsed_data_from_file(char* filename)
+{
+ FILE* file;
+ errno_t err = fopen_s(&file, filename, "r");
+
+ if (err != 0) {
+ fprintf(stderr, "ERROR: Couldn't open file '%s' for reading: %s\n", filename, strerror(err));
+ return NULL;
+ }
+
+ char line[256];
+
+ {
+ unsigned short vendor_id = 0;
+ unsigned short product_id = 0;
+ unsigned short usage = 0;
+ unsigned short usage_page = 0;
+ unsigned short release_number = 0;
+ int interface_number = -1;
+ BOOLEAN header_read_success = FALSE;
+ char manufacturer_string[128];
+ manufacturer_string[0] = '\0';
+ char product_string[128];
+ product_string[0] = '\0';
+ // char path[128];
+ // path[0] = '\0';
+
+ while (fgets(line, sizeof(line), file) != NULL) {
+ if (line[0] == '\r' || line[0] == '\n') {
+ line[0] = '\0';
+ }
+ if (line[0] == '\0') {
+ // read the 'metadata' only until the first empty line
+ header_read_success = TRUE;
+ break;
+ }
+ if (sscanf(line, "dev->vendor_id = 0x%04hX\n", &vendor_id)) continue;
+ if (sscanf(line, "dev->product_id = 0x%04hX\n", &product_id)) continue;
+ if (sscanf(line, "dev->usage_page = 0x%04hX\n", &usage_page)) continue;
+ if (sscanf(line, "dev->usage = 0x%04hX\n", &usage)) continue;
+ if (sscanf(line, "dev->manufacturer_string = \"%127[^\"\n]", manufacturer_string)) continue;
+ if (sscanf(line, "dev->product_string = \"%127[^\"\n]", product_string)) continue;
+ if (sscanf(line, "dev->release_number = 0x%04hX\n", &release_number)) continue;
+ if (sscanf(line, "dev->interface_number = %d\n", &interface_number)) continue;
+ // if (sscanf(line, "dev->path = \"%127[^\"]\n", path)) continue;
+ }
+ if (!header_read_success) {
+ fprintf(stderr, "ERROR: Couldn't read PP Data header (missing newline)\n");
+ fclose(file);
+ return NULL;
+ }
+ printf("'Virtual' Device Read: %04hx %04hx\n", vendor_id, product_id);
+ if (manufacturer_string[0] != '\0') {
+ printf(" Manufacturer: %s\n", manufacturer_string);
+ }
+ if (product_string[0] != '\0') {
+ printf(" Product: %s\n", product_string);
+ }
+ printf(" Release: %hx\n", release_number);
+ printf(" Interface: %d\n", interface_number);
+ printf(" Usage (page): 0x%hx (0x%hx)\n", usage, usage_page);
+ }
+
+ hidp_preparsed_data static_pp_data;
+ memset(&static_pp_data, 0, sizeof(static_pp_data));
+ hidp_preparsed_data *pp_data = &static_pp_data;
+
+ unsigned int rt_idx;
+ unsigned int caps_idx;
+ unsigned int token_idx;
+ unsigned int coll_idx;
+ USAGE temp_usage;
+ BOOLEAN temp_boolean[3];
+ UCHAR temp_uchar[3];
+ USHORT temp_ushort;
+ ULONG temp_ulong;
+ LONG temp_long;
+
+ USHORT FirstByteOfLinkCollectionArray = 0;
+ USHORT NumberLinkCollectionNodes = 0;
+
+ while (fgets(line, sizeof(line), file) != NULL) {
+ if (line[0] == '#')
+ continue;
+
+ if (FirstByteOfLinkCollectionArray != 0 && NumberLinkCollectionNodes != 0) {
+ size_t size_of_preparsed_data = offsetof(hidp_preparsed_data, caps) + FirstByteOfLinkCollectionArray + (NumberLinkCollectionNodes * sizeof(hid_pp_link_collection_node));
+ pp_data->FirstByteOfLinkCollectionArray = FirstByteOfLinkCollectionArray;
+ pp_data->NumberLinkCollectionNodes = NumberLinkCollectionNodes;
+ FirstByteOfLinkCollectionArray = 0;
+ NumberLinkCollectionNodes = 0;
+ pp_data = malloc(size_of_preparsed_data);
+ memcpy(pp_data, &static_pp_data, sizeof(static_pp_data));
+ }
+
+ if (sscanf(line, "pp_data->MagicKey = 0x%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX\n", &pp_data->MagicKey[0], &pp_data->MagicKey[1], &pp_data->MagicKey[2], &pp_data->MagicKey[3], &pp_data->MagicKey[4], &pp_data->MagicKey[5], &pp_data->MagicKey[6], &pp_data->MagicKey[7])) continue;
+ if (sscanf(line, "pp_data->Usage = 0x%04hX\n", &pp_data->Usage)) continue;
+ if (sscanf(line, "pp_data->UsagePage = 0x%04hX\n", &pp_data->UsagePage)) continue;
+ if (sscanf(line, "pp_data->Reserved = 0x%04hX%04hX\n", &pp_data->Reserved[0], &pp_data->Reserved[1])) continue;
+
+ if (sscanf(line, "pp_data->caps_info[%d]", &rt_idx) == 1) {
+ const size_t caps_info_count = sizeof(pp_data->caps_info) / sizeof(pp_data->caps_info[0]);
+ if (rt_idx >= caps_info_count) {
+ fprintf(stderr, "Broken pp_data file, pp_data->caps_info[<idx>] can have at most %zu elements, accessing %ud, (%s)", caps_info_count, rt_idx, line);
+ continue;
+ }
+ if (sscanf(line, "pp_data->caps_info[%d]->FirstCap = %hu\n", &rt_idx, &temp_ushort) == 2) {
+ pp_data->caps_info[rt_idx].FirstCap = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->caps_info[%d]->LastCap = %hu\n", &rt_idx, &temp_ushort) == 2) {
+ pp_data->caps_info[rt_idx].LastCap = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->caps_info[%d]->NumberOfCaps = %hu\n", &rt_idx, &temp_ushort) == 2) {
+ pp_data->caps_info[rt_idx].NumberOfCaps = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->caps_info[%d]->ReportByteLength = %hu\n", &rt_idx, &temp_ushort) == 2) {
+ pp_data->caps_info[rt_idx].ReportByteLength = temp_ushort;
+ continue;
+ }
+ fprintf(stderr, "Ignorring unimplemented caps_info field: %s", line);
+ continue;
+ }
+
+ if (sscanf(line, "pp_data->FirstByteOfLinkCollectionArray = 0x%04hX\n", &FirstByteOfLinkCollectionArray)) {
+ continue;
+ }
+ if (sscanf(line, "pp_data->NumberLinkCollectionNodes = %hu\n", &NumberLinkCollectionNodes)) {
+ continue;
+ }
+
+ if (sscanf(line, "pp_data->cap[%d]", &caps_idx) == 1) {
+ if (pp_data->FirstByteOfLinkCollectionArray == 0) {
+ fprintf(stderr, "Error reading pp_data file (%s): FirstByteOfLinkCollectionArray is 0 or not reported yet\n", line);
+ continue;
+ }
+ if ((caps_idx + 1) * sizeof(hid_pp_cap) > pp_data->FirstByteOfLinkCollectionArray) {
+ fprintf(stderr, "Error reading pp_data file (%s): the caps index (%u) is out of pp_data bytes boundary (%hu vs %hu)\n", line, caps_idx, (unsigned short) ((caps_idx + 1) * sizeof(hid_pp_cap)), pp_data->FirstByteOfLinkCollectionArray);
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->UsagePage = 0x%04hX\n", &caps_idx, &temp_usage) == 2) {
+ pp_data->caps[caps_idx].UsagePage = temp_usage;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->ReportID = 0x%02hhX\n", &caps_idx, &temp_uchar[0]) == 2) {
+ pp_data->caps[caps_idx].ReportID = temp_uchar[0];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->BitPosition = %hhu\n", &caps_idx, &temp_uchar[0]) == 2) {
+ pp_data->caps[caps_idx].BitPosition = temp_uchar[0];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->BitSize = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].ReportSize = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->ReportCount = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].ReportCount = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->BytePosition = 0x%04hX\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].BytePosition = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->BitCount = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].BitCount = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->BitField = 0x%02lX\n", &caps_idx, &temp_ulong) == 2) {
+ pp_data->caps[caps_idx].BitField = temp_ulong;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NextBytePosition = 0x%04hX\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].NextBytePosition = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->LinkCollection = 0x%04hX\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].LinkCollection = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->LinkUsagePage = 0x%04hX\n", &caps_idx, &temp_usage) == 2) {
+ pp_data->caps[caps_idx].LinkUsagePage = temp_usage;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->LinkUsage = 0x%04hX\n", &caps_idx, &temp_usage) == 2) {
+ pp_data->caps[caps_idx].LinkUsage = temp_usage;
+ continue;
+ }
+
+ // 8 Flags in one byte
+ if (sscanf(line, "pp_data->cap[%d]->IsMultipleItemsForArray = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) {
+ pp_data->caps[caps_idx].IsMultipleItemsForArray = temp_boolean[0];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->IsButtonCap = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) {
+ pp_data->caps[caps_idx].IsButtonCap = temp_boolean[0];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->IsPadding = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) {
+ pp_data->caps[caps_idx].IsPadding = temp_boolean[0];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->IsAbsolute = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) {
+ pp_data->caps[caps_idx].IsAbsolute = temp_boolean[0];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->IsRange = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) {
+ pp_data->caps[caps_idx].IsRange = temp_boolean[0];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->IsAlias = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) {
+ pp_data->caps[caps_idx].IsAlias = temp_boolean[0];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->IsStringRange = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) {
+ pp_data->caps[caps_idx].IsStringRange = temp_boolean[0];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->IsDesignatorRange = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) {
+ pp_data->caps[caps_idx].IsDesignatorRange = temp_boolean[0];
+ continue;
+ }
+
+ if (sscanf(line, "pp_data->cap[%d]->Reserved1 = 0x%hhu%hhu%hhu\n", &caps_idx, &temp_uchar[0], &temp_uchar[1], &temp_uchar[2]) == 4) {
+ pp_data->caps[caps_idx].Reserved1[0] = temp_uchar[0];
+ pp_data->caps[caps_idx].Reserved1[1] = temp_uchar[1];
+ pp_data->caps[caps_idx].Reserved1[2] = temp_uchar[2];
+ continue;
+ }
+
+ if (sscanf(line, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d]", &caps_idx, &token_idx) == 2) {
+ const size_t unknown_tokens_count = sizeof(pp_data->caps[0].UnknownTokens) / sizeof(pp_data->caps[0].UnknownTokens[0]);
+ if (token_idx >= unknown_tokens_count) {
+ fprintf(stderr, "Broken pp_data file, pp_data->caps[<idx>].UnknownTokens[<idx>] can have at most %zu elements, accessing %ud, (%s)", unknown_tokens_count, token_idx, line);
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d].Token = 0x%02hhX\n", &caps_idx, &token_idx, &temp_uchar[0]) == 3) {
+ pp_data->caps[caps_idx].UnknownTokens[token_idx].Token = temp_uchar[0];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d].Reserved = 0x%02hhX%02hhX%02hhX\n", &caps_idx, &token_idx, &temp_uchar[0], &temp_uchar[1], &temp_uchar[2]) == 5) {
+ pp_data->caps[caps_idx].UnknownTokens[token_idx].Reserved[0] = temp_uchar[0];
+ pp_data->caps[caps_idx].UnknownTokens[token_idx].Reserved[1] = temp_uchar[1];
+ pp_data->caps[caps_idx].UnknownTokens[token_idx].Reserved[2] = temp_uchar[2];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->pp_cap->UnknownTokens[%d].BitField = 0x%08lX\n", &caps_idx, &token_idx, &temp_ulong) == 3) {
+ pp_data->caps[caps_idx].UnknownTokens[token_idx].BitField = temp_ulong;
+ continue;
+ }
+ fprintf(stderr, "Ignorring unimplemented pp_data->cap[]->pp_cap->UnknownTokens field: %s", line);
+ continue;
+ }
+
+ // Range
+ if (sscanf(line, "pp_data->cap[%d]->Range.UsageMin = 0x%04hX\n", &caps_idx, &temp_usage) == 2) {
+ pp_data->caps[caps_idx].Range.UsageMin = temp_usage;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->Range.UsageMax = 0x%04hX\n", &caps_idx, &temp_usage) == 2) {
+ pp_data->caps[caps_idx].Range.UsageMax = temp_usage;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->Range.StringMin = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].Range.StringMin = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->Range.StringMax = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].Range.StringMax = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->Range.DesignatorMin = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].Range.DesignatorMin = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->Range.DesignatorMax = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].Range.DesignatorMax = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->Range.DataIndexMin = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].Range.DataIndexMin = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->Range.DataIndexMax = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].Range.DataIndexMax = temp_ushort;
+ continue;
+ }
+
+ // NotRange
+ if (sscanf(line, "pp_data->cap[%d]->NotRange.Usage = 0x%04hX\n", &caps_idx, &temp_usage) == 2) {
+ pp_data->caps[caps_idx].NotRange.Usage = temp_usage;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NotRange.Reserved1 = 0x%04hX\n", &caps_idx, &temp_usage) == 2) {
+ pp_data->caps[caps_idx].NotRange.Reserved1 = temp_usage;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NotRange.StringIndex = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].NotRange.StringIndex = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NotRange.Reserved2 = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].NotRange.Reserved2 = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NotRange.DesignatorIndex = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].NotRange.DesignatorIndex = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NotRange.Reserved3 = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].NotRange.Reserved3 = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NotRange.DataIndex = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].NotRange.DataIndex = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NotRange.Reserved4 = %hu\n", &caps_idx, &temp_ushort) == 2) {
+ pp_data->caps[caps_idx].NotRange.Reserved4 = temp_ushort;
+ continue;
+ }
+
+ // Button
+ if (sscanf(line, "pp_data->cap[%d]->Button.LogicalMin = %ld\n", &caps_idx, &temp_long) == 2) {
+ pp_data->caps[caps_idx].Button.LogicalMin = temp_long;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->Button.LogicalMax = %ld\n", &caps_idx, &temp_long) == 2) {
+ pp_data->caps[caps_idx].Button.LogicalMax = temp_long;
+ continue;
+ }
+
+ // NotButton
+ if (sscanf(line, "pp_data->cap[%d]->NotButton.HasNull = %hhu\n", &caps_idx, &temp_boolean[0]) == 2) {
+ pp_data->caps[caps_idx].NotButton.HasNull = temp_boolean[0];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NotButton.Reserved4 = 0x%02hhX%02hhX%02hhX\n", &caps_idx, &temp_uchar[0], &temp_uchar[1], &temp_uchar[2]) == 4) {
+ pp_data->caps[caps_idx].NotButton.Reserved4[0] = temp_uchar[0];
+ pp_data->caps[caps_idx].NotButton.Reserved4[1] = temp_uchar[1];
+ pp_data->caps[caps_idx].NotButton.Reserved4[2] = temp_uchar[2];
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NotButton.LogicalMin = %ld\n", &caps_idx, &temp_long) == 2) {
+ pp_data->caps[caps_idx].NotButton.LogicalMin = temp_long;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NotButton.LogicalMax = %ld\n", &caps_idx, &temp_long) == 2) {
+ pp_data->caps[caps_idx].NotButton.LogicalMax = temp_long;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NotButton.PhysicalMin = %ld\n", &caps_idx, &temp_long) == 2) {
+ pp_data->caps[caps_idx].NotButton.PhysicalMin = temp_long;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->NotButton.PhysicalMax = %ld\n", &caps_idx, &temp_long) == 2) {
+ pp_data->caps[caps_idx].NotButton.PhysicalMax = temp_long;
+ continue;
+ }
+
+ if (sscanf(line, "pp_data->cap[%d]->Units = %lu\n", &caps_idx, &temp_ulong) == 2) {
+ pp_data->caps[caps_idx].Units = temp_ulong;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->UnitsExp = %lu\n", &caps_idx, &temp_ulong) == 2) {
+ pp_data->caps[caps_idx].UnitsExp = temp_ulong;
+ continue;
+ }
+ if (sscanf(line, "pp_data->cap[%d]->Reserved1 = 0x%02hhu%02hhu%02hhu\n", &coll_idx, &temp_uchar[0], &temp_uchar[1], &temp_uchar[2]) == 4) {
+ pp_data->caps[caps_idx].Reserved1[0] = temp_uchar[0];
+ pp_data->caps[caps_idx].Reserved1[1] = temp_uchar[1];
+ pp_data->caps[caps_idx].Reserved1[2] = temp_uchar[2];
+ continue;
+ }
+ fprintf(stderr, "Ignorring unimplemented cap field: %s", line);
+ continue;
+ }
+
+ if (sscanf(line, "pp_data->LinkCollectionArray[%d]", &coll_idx) == 1) {
+ if (pp_data->FirstByteOfLinkCollectionArray == 0 || pp_data->NumberLinkCollectionNodes == 0) {
+ fprintf(stderr, "Error reading pp_data file (%s): FirstByteOfLinkCollectionArray or NumberLinkCollectionNodes is 0 or not reported yet\n", line);
+ continue;
+ }
+ if (coll_idx >= pp_data->NumberLinkCollectionNodes) {
+ fprintf(stderr, "Error reading pp_data file (%s): the LinkCollection index (%u) is out of boundary (%hu)\n", line, coll_idx, pp_data->NumberLinkCollectionNodes);
+ continue;
+ }
+ phid_pp_link_collection_node pcoll = (phid_pp_link_collection_node)(((unsigned char*)&pp_data->caps[0]) + pp_data->FirstByteOfLinkCollectionArray);
+ if (sscanf(line, "pp_data->LinkCollectionArray[%d]->LinkUsage = 0x%04hX\n", &coll_idx, &temp_usage) == 2) {
+ pcoll[coll_idx].LinkUsage = temp_usage;
+ continue;
+ }
+ if (sscanf(line, "pp_data->LinkCollectionArray[%d]->LinkUsagePage = 0x%04hX\n", &coll_idx, &temp_usage) == 2) {
+ pcoll[coll_idx].LinkUsagePage = temp_usage;
+ continue;
+ }
+ if (sscanf(line, "pp_data->LinkCollectionArray[%d]->Parent = %hu\n", &coll_idx, &temp_ushort) == 2) {
+ pcoll[coll_idx].Parent = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->LinkCollectionArray[%d]->NumberOfChildren = %hu\n", &coll_idx, &temp_ushort) == 2) {
+ pcoll[coll_idx].NumberOfChildren = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->LinkCollectionArray[%d]->NextSibling = %hu\n", &coll_idx, &temp_ushort) == 2) {
+ pcoll[coll_idx].NextSibling = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->LinkCollectionArray[%d]->FirstChild = %hu\n", &coll_idx, &temp_ushort) == 2) {
+ pcoll[coll_idx].FirstChild = temp_ushort;
+ continue;
+ }
+ if (sscanf(line, "pp_data->LinkCollectionArray[%d]->CollectionType = %ld\n", &coll_idx, &temp_ulong) == 2) {
+ pcoll[coll_idx].CollectionType = temp_ulong;
+ continue;
+ }
+ if (sscanf(line, "pp_data->LinkCollectionArray[%d]->IsAlias = %ld\n", &coll_idx, &temp_ulong) == 2) {
+ pcoll[coll_idx].IsAlias = temp_ulong;
+ continue;
+ }
+ if (sscanf(line, "pp_data->LinkCollectionArray[%d]->Reserved = %ld\n", &coll_idx, &temp_ulong) == 2) {
+ pcoll[coll_idx].Reserved = temp_ulong;
+ continue;
+ }
+ fprintf(stderr, "Ignorring unimplemented LinkCollectionArray field: %s", line);
+ continue;
+ }
+ }
+
+//end:
+ fclose(file);
+
+ if (pp_data == &static_pp_data) {
+ return NULL;
+ }
+
+ return pp_data;
+}
+
+static BOOLEAN read_hex_data_from_text_file(const char *filename, unsigned char *data_out, size_t data_size, size_t *actual_read)
+{
+ size_t read_index = 0;
+ FILE* file = NULL;
+ errno_t err = fopen_s(&file, filename, "r");
+ if (err != 0) {
+ fprintf(stderr, "ERROR: Couldn't open file '%s' for reading: %s\n", filename, strerror(err));
+ return FALSE;
+ }
+
+ BOOLEAN result = TRUE;
+ unsigned int val;
+ char buf[16];
+ while (fscanf(file, "%15s", buf) == 1) {
+ if (sscanf(buf, "0x%X", &val) != 1) {
+ fprintf(stderr, "Invalid HEX text ('%s') file, got %s\n", filename, buf);
+ result = FALSE;
+ goto end;
+ }
+
+ if (read_index >= data_size) {
+ fprintf(stderr, "Buffer for file read is too small. Got only %zu bytes to read '%s'\n", data_size, filename);
+ result = FALSE;
+ goto end;
+ }
+
+ if (val > (unsigned char)-1) {
+ fprintf(stderr, "Invalid HEX text ('%s') file, got a value of: %u\n", filename, val);
+ result = FALSE;
+ goto end;
+ }
+
+ data_out[read_index] = (unsigned char) val;
+
+ read_index++;
+ }
+
+ if (!feof(file)) {
+ fprintf(stderr, "Invalid HEX text ('%s') file - failed to read all values\n", filename);
+ result = FALSE;
+ goto end;
+ }
+
+ *actual_read = read_index;
+
+end:
+ fclose(file);
+ return result;
+}
+
+
+int main(int argc, char* argv[])
+{
+ if (argc != 3) {
+ fprintf(stderr, "Expected 2 arguments for the test ('<>.pp_data' and '<>_expected.rpt_desc'), got: %d\n", argc - 1);
+ return EXIT_FAILURE;
+ }
+
+ printf("Checking: '%s' / '%s'\n", argv[1], argv[2]);
+
+ hidp_preparsed_data *pp_data = alloc_preparsed_data_from_file(argv[1]);
+ if (pp_data == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ int result = EXIT_SUCCESS;
+
+ unsigned char report_descriptor[HID_API_MAX_REPORT_DESCRIPTOR_SIZE];
+
+ int res = hid_winapi_descriptor_reconstruct_pp_data(pp_data, report_descriptor, sizeof(report_descriptor));
+
+ if (res < 0) {
+ result = EXIT_FAILURE;
+ fprintf(stderr, "Failed to reconstruct descriptor");
+ goto end;
+ }
+ size_t report_descriptor_size = (size_t) res;
+
+ unsigned char expected_report_descriptor[HID_API_MAX_REPORT_DESCRIPTOR_SIZE];
+ size_t expected_report_descriptor_size = 0;
+ if (!read_hex_data_from_text_file(argv[2], expected_report_descriptor, sizeof(expected_report_descriptor), &expected_report_descriptor_size)) {
+ result = EXIT_FAILURE;
+ goto end;
+ }
+
+ if (report_descriptor_size == expected_report_descriptor_size) {
+ if (memcmp(report_descriptor, expected_report_descriptor, report_descriptor_size) == 0) {
+ printf("Reconstructed Report Descriptor matches the expected descriptor\n");
+ goto end;
+ }
+ else {
+ result = EXIT_FAILURE;
+ fprintf(stderr, "Reconstructed Report Descriptor has different content than expected\n");
+ }
+ }
+ else {
+ result = EXIT_FAILURE;
+ fprintf(stderr, "Reconstructed Report Descriptor has different size: %zu when expected %zu\n", report_descriptor_size, expected_report_descriptor_size);
+ }
+
+ printf(" Reconstructed Report Descriptor:\n");
+ for (int i = 0; i < res; i++) {
+ printf("0x%02X, ", report_descriptor[i]);
+ }
+ printf("\n");
+ fflush(stdout);
+
+end:
+ free(pp_data);
+ return result;
+}