old data
This commit is contained in:
@@ -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()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
|
||||
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
|
||||
@@ -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>
|
||||
@@ -0,0 +1,86 @@
|
||||
/*******************************************************
|
||||
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_CFGMGR32_H
|
||||
#define HIDAPI_CFGMGR32_H
|
||||
|
||||
#ifdef HIDAPI_USE_DDK
|
||||
|
||||
#include <cfgmgr32.h>
|
||||
#include <initguid.h>
|
||||
#include <devpkey.h>
|
||||
#include <propkey.h>
|
||||
|
||||
#else
|
||||
|
||||
/* This part of the header mimics cfgmgr32.h,
|
||||
but only what is used by HIDAPI */
|
||||
|
||||
//#include <initguid.h>
|
||||
#include <devpropdef.h>
|
||||
//#include <propkeydef.h>
|
||||
|
||||
#ifndef PROPERTYKEY_DEFINED
|
||||
#define PROPERTYKEY_DEFINED
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GUID fmtid;
|
||||
DWORD pid;
|
||||
} PROPERTYKEY;
|
||||
|
||||
#endif /* PROPERTYKEY_DEFINED */
|
||||
|
||||
typedef DWORD RETURN_TYPE;
|
||||
typedef RETURN_TYPE CONFIGRET;
|
||||
typedef DWORD DEVNODE, DEVINST;
|
||||
typedef DEVNODE* PDEVNODE, * PDEVINST;
|
||||
typedef WCHAR* DEVNODEID_W, * DEVINSTID_W;
|
||||
|
||||
#define CR_SUCCESS (0x00000000)
|
||||
#define CR_BUFFER_SMALL (0x0000001A)
|
||||
#define CR_FAILURE (0x00000013)
|
||||
|
||||
#define CM_LOCATE_DEVNODE_NORMAL 0x00000000
|
||||
|
||||
#define CM_GET_DEVICE_INTERFACE_LIST_PRESENT (0x00000000)
|
||||
|
||||
typedef CONFIGRET(__stdcall* CM_Locate_DevNodeW_)(PDEVINST pdnDevInst, DEVINSTID_W pDeviceID, ULONG ulFlags);
|
||||
typedef CONFIGRET(__stdcall* CM_Get_Parent_)(PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags);
|
||||
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* /*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_Manufacturer = { { 0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0} }, 13 }; // 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
|
||||
|
||||
// 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
|
||||
static PROPERTYKEY PKEY_DeviceInterface_Bluetooth_ModelNumber = { { 0x2BD67D8B, 0x8BEB, 0x48D5, {0x87, 0xE0, 0x6C, 0xDA, 0x34, 0x28, 0x04, 0x0A} }, 5 }; // DEVPROP_TYPE_STRING
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HIDAPI_CFGMGR32_H */
|
||||
@@ -0,0 +1,990 @@
|
||||
/*******************************************************
|
||||
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 rpt_desc Pointer to report descriptor buffer struct.
|
||||
*
|
||||
* @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;
|
||||
rpt_desc.buf = buf;
|
||||
rpt_desc.buf_size = buf_size;
|
||||
rpt_desc.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 hierarchy 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 hierarchy, 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 the 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];
|
||||
struct rd_main_item_node **last_report_item_lookup[NUM_OF_HIDP_REPORT_TYPES];
|
||||
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
||||
last_bit_position[rt_idx] = malloc(256 * sizeof(*last_bit_position[rt_idx]));
|
||||
last_report_item_lookup[rt_idx] = malloc(256 * sizeof(*last_report_item_lookup[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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(last_bit_position[rt_idx]);
|
||||
free(last_report_item_lookup[rt_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;
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*******************************************************
|
||||
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 "SDL_internal.h"
|
||||
|
||||
#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"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4200)
|
||||
#pragma warning(disable: 4201)
|
||||
#pragma warning(disable: 4214)
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "hidapi_hidsdi.h"
|
||||
/*#include <assert.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;
|
||||
|
||||
// Note: This is risk-reduction-measure for this specific struct, as it has ULONG bit-field.
|
||||
// Although very unlikely, it might still be possible that the compiler creates a memory layout that is
|
||||
// not binary compatile.
|
||||
// Other structs are not checked at the time of writing.
|
||||
//static_assert(sizeof(struct hid_pp_link_collection_node_) == 16,
|
||||
// "Size of struct hid_pp_link_collection_node_ not as expected. This might break binary compatibility");
|
||||
SDL_COMPILE_TIME_ASSERT(hid_pp_link_collection_node_, sizeof(struct hid_pp_link_collection_node_) == 16);
|
||||
|
||||
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;
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// MINGW fails with: Flexible array member in union not supported
|
||||
// Solution: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
|
||||
union {
|
||||
#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
#endif
|
||||
hid_pp_cap caps[0];
|
||||
hid_pp_link_collection_node LinkCollectionArray[0];
|
||||
#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
};
|
||||
#else
|
||||
union {
|
||||
hid_pp_cap caps[];
|
||||
hid_pp_link_collection_node LinkCollectionArray[];
|
||||
};
|
||||
#endif
|
||||
|
||||
} hidp_preparsed_data;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
/*******************************************************
|
||||
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_HIDCLASS_H
|
||||
#define HIDAPI_HIDCLASS_H
|
||||
|
||||
#ifdef HIDAPI_USE_DDK
|
||||
|
||||
#include <hidclass.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <winioctl.h>
|
||||
|
||||
/* This part of the header mimics hidclass.h,
|
||||
but only what is used by HIDAPI */
|
||||
|
||||
#define HID_OUT_CTL_CODE(id) CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
|
||||
#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100)
|
||||
#define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HIDAPI_HIDCLASS_H */
|
||||
@@ -0,0 +1,72 @@
|
||||
/*******************************************************
|
||||
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_HIDPI_H
|
||||
#define HIDAPI_HIDPI_H
|
||||
|
||||
#ifdef HIDAPI_USE_DDK
|
||||
|
||||
#include <hidpi.h>
|
||||
|
||||
#else
|
||||
|
||||
/* 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
|
||||
{
|
||||
USAGE Usage;
|
||||
USAGE UsagePage;
|
||||
USHORT InputReportByteLength;
|
||||
USHORT OutputReportByteLength;
|
||||
USHORT FeatureReportByteLength;
|
||||
USHORT Reserved[17];
|
||||
|
||||
USHORT NumberLinkCollectionNodes;
|
||||
|
||||
USHORT NumberInputButtonCaps;
|
||||
USHORT NumberInputValueCaps;
|
||||
USHORT NumberInputDataIndices;
|
||||
|
||||
USHORT NumberOutputButtonCaps;
|
||||
USHORT NumberOutputValueCaps;
|
||||
USHORT NumberOutputDataIndices;
|
||||
|
||||
USHORT NumberFeatureButtonCaps;
|
||||
USHORT NumberFeatureValueCaps;
|
||||
USHORT NumberFeatureDataIndices;
|
||||
} HIDP_CAPS, *PHIDP_CAPS;
|
||||
|
||||
#define HIDP_STATUS_SUCCESS 0x00110000
|
||||
#define HIDP_STATUS_INVALID_PREPARSED_DATA 0xc0110001
|
||||
|
||||
typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, PHIDP_CAPS caps);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HIDAPI_HIDPI_H */
|
||||
@@ -0,0 +1,59 @@
|
||||
/*******************************************************
|
||||
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_HIDSDI_H
|
||||
#define HIDAPI_HIDSDI_H
|
||||
|
||||
#ifdef HIDAPI_USE_DDK
|
||||
|
||||
#include <hidsdi.h>
|
||||
|
||||
#else
|
||||
|
||||
/* This part of the header mimics hidsdi.h,
|
||||
but only what is used by HIDAPI */
|
||||
|
||||
typedef USHORT USAGE;
|
||||
|
||||
#include "hidapi_hidpi.h"
|
||||
|
||||
typedef struct _HIDD_ATTRIBUTES{
|
||||
ULONG Size;
|
||||
USHORT VendorID;
|
||||
USHORT ProductID;
|
||||
USHORT VersionNumber;
|
||||
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
|
||||
|
||||
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);
|
||||
typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
|
||||
typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
|
||||
typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length);
|
||||
typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length);
|
||||
typedef BOOLEAN (__stdcall *HidD_GetInputReport_)(HANDLE handle, PVOID data, ULONG length);
|
||||
typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len);
|
||||
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 HidDeviceObject, PVOID ReportBuffer, ULONG ReportBufferLength);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HIDAPI_HIDSDI_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/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 container_id 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
|
||||
@@ -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>
|
||||
@@ -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}"
|
||||
)
|
||||
@@ -0,0 +1,238 @@
|
||||
#if defined(__MINGW32__)
|
||||
// Needed for %hh
|
||||
#define __USE_MINGW_ANSI_STDIO 1
|
||||
#endif
|
||||
|
||||
#include <hid.c>
|
||||
#include <../windows/hidapi_descriptor_reconstruct.h>
|
||||
|
||||
#include <hidapi.h>
|
||||
|
||||
void dump_hid_pp_cap(FILE* file, phid_pp_cap pp_cap, unsigned int cap_idx) {
|
||||
fprintf(file, "pp_data->cap[%u]->UsagePage = 0x%04hX\n", cap_idx, pp_cap->UsagePage);
|
||||
fprintf(file, "pp_data->cap[%u]->ReportID = 0x%02hhX\n", cap_idx, pp_cap->ReportID);
|
||||
fprintf(file, "pp_data->cap[%u]->BitPosition = %hhu\n", cap_idx, pp_cap->BitPosition);
|
||||
fprintf(file, "pp_data->cap[%u]->BitSize = %hu\n", cap_idx, pp_cap->ReportSize);
|
||||
fprintf(file, "pp_data->cap[%u]->ReportCount = %hu\n", cap_idx, pp_cap->ReportCount);
|
||||
fprintf(file, "pp_data->cap[%u]->BytePosition = 0x%04hX\n", cap_idx, pp_cap->BytePosition);
|
||||
fprintf(file, "pp_data->cap[%u]->BitCount = %hu\n", cap_idx, pp_cap->BitCount);
|
||||
fprintf(file, "pp_data->cap[%u]->BitField = 0x%02lX\n", cap_idx, pp_cap->BitField);
|
||||
fprintf(file, "pp_data->cap[%u]->NextBytePosition = 0x%04hX\n", cap_idx, pp_cap->NextBytePosition);
|
||||
fprintf(file, "pp_data->cap[%u]->LinkCollection = 0x%04hX\n", cap_idx, pp_cap->LinkCollection);
|
||||
fprintf(file, "pp_data->cap[%u]->LinkUsagePage = 0x%04hX\n", cap_idx, pp_cap->LinkUsagePage);
|
||||
fprintf(file, "pp_data->cap[%u]->LinkUsage = 0x%04hX\n", cap_idx, pp_cap->LinkUsage);
|
||||
|
||||
// 8 Flags in one byte
|
||||
fprintf(file, "pp_data->cap[%u]->IsMultipleItemsForArray = %hhu\n", cap_idx, pp_cap->IsMultipleItemsForArray);
|
||||
fprintf(file, "pp_data->cap[%u]->IsButtonCap = %hhu\n", cap_idx, pp_cap->IsButtonCap);
|
||||
fprintf(file, "pp_data->cap[%u]->IsPadding = %hhu\n", cap_idx, pp_cap->IsPadding);
|
||||
fprintf(file, "pp_data->cap[%u]->IsAbsolute = %hhu\n", cap_idx, pp_cap->IsAbsolute);
|
||||
fprintf(file, "pp_data->cap[%u]->IsRange = %hhu\n", cap_idx, pp_cap->IsRange);
|
||||
fprintf(file, "pp_data->cap[%u]->IsAlias = %hhu\n", cap_idx, pp_cap->IsAlias);
|
||||
fprintf(file, "pp_data->cap[%u]->IsStringRange = %hhu\n", cap_idx, pp_cap->IsStringRange);
|
||||
fprintf(file, "pp_data->cap[%u]->IsDesignatorRange = %hhu\n", cap_idx, pp_cap->IsDesignatorRange);
|
||||
|
||||
fprintf(file, "pp_data->cap[%u]->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[%u]->pp_cap->UnknownTokens[%d].Token = 0x%02hhX\n", cap_idx, token_idx, pp_cap->UnknownTokens[token_idx].Token);
|
||||
fprintf(file, "pp_data->cap[%u]->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[%u]->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[%u]->Range.UsageMin = 0x%04hX\n", cap_idx, pp_cap->Range.UsageMin);
|
||||
fprintf(file, "pp_data->cap[%u]->Range.UsageMax = 0x%04hX\n", cap_idx, pp_cap->Range.UsageMax);
|
||||
fprintf(file, "pp_data->cap[%u]->Range.StringMin = %hu\n", cap_idx, pp_cap->Range.StringMin);
|
||||
fprintf(file, "pp_data->cap[%u]->Range.StringMax = %hu\n", cap_idx, pp_cap->Range.StringMax);
|
||||
fprintf(file, "pp_data->cap[%u]->Range.DesignatorMin = %hu\n", cap_idx, pp_cap->Range.DesignatorMin);
|
||||
fprintf(file, "pp_data->cap[%u]->Range.DesignatorMax = %hu\n", cap_idx, pp_cap->Range.DesignatorMax);
|
||||
fprintf(file, "pp_data->cap[%u]->Range.DataIndexMin = %hu\n", cap_idx, pp_cap->Range.DataIndexMin);
|
||||
fprintf(file, "pp_data->cap[%u]->Range.DataIndexMax = %hu\n", cap_idx, pp_cap->Range.DataIndexMax);
|
||||
}
|
||||
else {
|
||||
fprintf(file, "pp_data->cap[%u]->NotRange.Usage = 0x%04hX\n", cap_idx, pp_cap->NotRange.Usage);
|
||||
fprintf(file, "pp_data->cap[%u]->NotRange.Reserved1 = 0x%04hX\n", cap_idx, pp_cap->NotRange.Reserved1);
|
||||
fprintf(file, "pp_data->cap[%u]->NotRange.StringIndex = %hu\n", cap_idx, pp_cap->NotRange.StringIndex);
|
||||
fprintf(file, "pp_data->cap[%u]->NotRange.Reserved2 = %hu\n", cap_idx, pp_cap->NotRange.Reserved2);
|
||||
fprintf(file, "pp_data->cap[%u]->NotRange.DesignatorIndex = %hu\n", cap_idx, pp_cap->NotRange.DesignatorIndex);
|
||||
fprintf(file, "pp_data->cap[%u]->NotRange.Reserved3 = %hu\n", cap_idx, pp_cap->NotRange.Reserved3);
|
||||
fprintf(file, "pp_data->cap[%u]->NotRange.DataIndex = %hu\n", cap_idx, pp_cap->NotRange.DataIndex);
|
||||
fprintf(file, "pp_data->cap[%u]->NotRange.Reserved4 = %hu\n", cap_idx, pp_cap->NotRange.Reserved4);
|
||||
}
|
||||
|
||||
if (pp_cap->IsButtonCap) {
|
||||
fprintf(file, "pp_data->cap[%u]->Button.LogicalMin = %ld\n", cap_idx, pp_cap->Button.LogicalMin);
|
||||
fprintf(file, "pp_data->cap[%u]->Button.LogicalMax = %ld\n", cap_idx, pp_cap->Button.LogicalMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "pp_data->cap[%u]->NotButton.HasNull = %hhu\n", cap_idx, pp_cap->NotButton.HasNull);
|
||||
fprintf(file, "pp_data->cap[%u]->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[%u]->NotButton.LogicalMin = %ld\n", cap_idx, pp_cap->NotButton.LogicalMin);
|
||||
fprintf(file, "pp_data->cap[%u]->NotButton.LogicalMax = %ld\n", cap_idx, pp_cap->NotButton.LogicalMax);
|
||||
fprintf(file, "pp_data->cap[%u]->NotButton.PhysicalMin = %ld\n", cap_idx, pp_cap->NotButton.PhysicalMin);
|
||||
fprintf(file, "pp_data->cap[%u]->NotButton.PhysicalMax = %ld\n", cap_idx, pp_cap->NotButton.PhysicalMax);
|
||||
};
|
||||
fprintf(file, "pp_data->cap[%u]->Units = %lu\n", cap_idx, pp_cap->Units);
|
||||
fprintf(file, "pp_data->cap[%u]->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[%u]->LinkUsage = 0x%04hX\n", coll_idx, pcoll->LinkUsage);
|
||||
fprintf(file, "pp_data->LinkCollectionArray[%u]->LinkUsagePage = 0x%04hX\n", coll_idx, pcoll->LinkUsagePage);
|
||||
fprintf(file, "pp_data->LinkCollectionArray[%u]->Parent = %hu\n", coll_idx, pcoll->Parent);
|
||||
fprintf(file, "pp_data->LinkCollectionArray[%u]->NumberOfChildren = %hu\n", coll_idx, pcoll->NumberOfChildren);
|
||||
fprintf(file, "pp_data->LinkCollectionArray[%u]->NextSibling = %hu\n", coll_idx, pcoll->NextSibling);
|
||||
fprintf(file, "pp_data->LinkCollectionArray[%u]->FirstChild = %hu\n", coll_idx, pcoll->FirstChild);
|
||||
// The compilers are not consistent on ULONG-bit-fields: They lose the unsinged or define them as int.
|
||||
// Thus just always cast them to unsinged int, which should be fine, as the biggest bit-field is 28 bit
|
||||
fprintf(file, "pp_data->LinkCollectionArray[%u]->CollectionType = %u\n", coll_idx, (unsigned int)(pcoll->CollectionType));
|
||||
fprintf(file, "pp_data->LinkCollectionArray[%u]->IsAlias = %u\n", coll_idx, (unsigned int)(pcoll->IsAlias));
|
||||
fprintf(file, "pp_data->LinkCollectionArray[%u]->Reserved = 0x%08X\n", coll_idx, (unsigned int)(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;
|
||||
}
|
||||
@@ -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()
|
||||
@@ -0,0 +1,563 @@
|
||||
#if defined(__MINGW32__)
|
||||
// Needed for %zu
|
||||
#define __USE_MINGW_ANSI_STDIO 1
|
||||
#endif
|
||||
|
||||
#include "../hidapi_descriptor_reconstruct.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
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[%u]", &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[%u]->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[%u]->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[%u]->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[%u]->ReportByteLength = %hu\n", &rt_idx, &temp_ushort) == 2) {
|
||||
pp_data->caps_info[rt_idx].ReportByteLength = temp_ushort;
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr, "Ignoring 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[%u]", &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[%u]->UsagePage = 0x%04hX\n", &caps_idx, &temp_usage) == 2) {
|
||||
pp_data->caps[caps_idx].UsagePage = temp_usage;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->cap[%u]->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[%u]->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[%u]->BitSize = %hu\n", &caps_idx, &temp_ushort) == 2) {
|
||||
pp_data->caps[caps_idx].ReportSize = temp_ushort;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->cap[%u]->ReportCount = %hu\n", &caps_idx, &temp_ushort) == 2) {
|
||||
pp_data->caps[caps_idx].ReportCount = temp_ushort;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->cap[%u]->BytePosition = 0x%04hX\n", &caps_idx, &temp_ushort) == 2) {
|
||||
pp_data->caps[caps_idx].BytePosition = temp_ushort;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->cap[%u]->BitCount = %hu\n", &caps_idx, &temp_ushort) == 2) {
|
||||
pp_data->caps[caps_idx].BitCount = temp_ushort;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->cap[%u]->BitField = 0x%02lX\n", &caps_idx, &temp_ulong) == 2) {
|
||||
pp_data->caps[caps_idx].BitField = temp_ulong;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->cap[%u]->NextBytePosition = 0x%04hX\n", &caps_idx, &temp_ushort) == 2) {
|
||||
pp_data->caps[caps_idx].NextBytePosition = temp_ushort;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->cap[%u]->LinkCollection = 0x%04hX\n", &caps_idx, &temp_ushort) == 2) {
|
||||
pp_data->caps[caps_idx].LinkCollection = temp_ushort;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->cap[%u]->LinkUsagePage = 0x%04hX\n", &caps_idx, &temp_usage) == 2) {
|
||||
pp_data->caps[caps_idx].LinkUsagePage = temp_usage;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->cap[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->pp_cap->UnknownTokens[%u]", &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[%u]->pp_cap->UnknownTokens[%u].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[%u]->pp_cap->UnknownTokens[%u].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[%u]->pp_cap->UnknownTokens[%u].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, "Ignoring unimplemented pp_data->cap[]->pp_cap->UnknownTokens field: %s", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Range
|
||||
if (sscanf(line, "pp_data->cap[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->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[%u]->Units = %lu\n", &caps_idx, &temp_ulong) == 2) {
|
||||
pp_data->caps[caps_idx].Units = temp_ulong;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->cap[%u]->UnitsExp = %lu\n", &caps_idx, &temp_ulong) == 2) {
|
||||
pp_data->caps[caps_idx].UnitsExp = temp_ulong;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->cap[%u]->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, "Ignoring unimplemented cap field: %s", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sscanf(line, "pp_data->LinkCollectionArray[%u]", &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[%u]->LinkUsage = 0x%04hX\n", &coll_idx, &temp_usage) == 2) {
|
||||
pcoll[coll_idx].LinkUsage = temp_usage;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->LinkCollectionArray[%u]->LinkUsagePage = 0x%04hX\n", &coll_idx, &temp_usage) == 2) {
|
||||
pcoll[coll_idx].LinkUsagePage = temp_usage;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->LinkCollectionArray[%u]->Parent = %hu\n", &coll_idx, &temp_ushort) == 2) {
|
||||
pcoll[coll_idx].Parent = temp_ushort;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->LinkCollectionArray[%u]->NumberOfChildren = %hu\n", &coll_idx, &temp_ushort) == 2) {
|
||||
pcoll[coll_idx].NumberOfChildren = temp_ushort;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->LinkCollectionArray[%u]->NextSibling = %hu\n", &coll_idx, &temp_ushort) == 2) {
|
||||
pcoll[coll_idx].NextSibling = temp_ushort;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->LinkCollectionArray[%u]->FirstChild = %hu\n", &coll_idx, &temp_ushort) == 2) {
|
||||
pcoll[coll_idx].FirstChild = temp_ushort;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->LinkCollectionArray[%u]->CollectionType = %lu\n", &coll_idx, &temp_ulong) == 2) {
|
||||
pcoll[coll_idx].CollectionType = temp_ulong;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->LinkCollectionArray[%u]->IsAlias = %lu\n", &coll_idx, &temp_ulong) == 2) {
|
||||
pcoll[coll_idx].IsAlias = temp_ulong;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "pp_data->LinkCollectionArray[%u]->Reserved = %lu\n", &coll_idx, &temp_ulong) == 2) {
|
||||
pcoll[coll_idx].Reserved = temp_ulong;
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr, "Ignoring 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;
|
||||
}
|
||||
Reference in New Issue
Block a user