From d1e00dac5ccc209020221fbc759080e31ae5cacd Mon Sep 17 00:00:00 2001 From: Johannes Anderwald Date: Wed, 4 Jan 2012 21:58:18 +0000 Subject: [PATCH] [USB-BRINGUP] - Implement a HID parsing library, which is based on Haiku`s exellent HID Stack - Implement HidP_FreeCollection, HidP_GetCaps, HidP_GetCollectionDescription, HidP_MaxUsageListLength, HidP_GetSpecificValueCaps, HidP_GetUsages, HidP_GetScaledUsageValue - hidparse driver is now implemented enough to support a hid mouse - Tested in VBox 4.1.4 + WinXP + ReactOS usbohci+ mouhid+ hidclass+ hidparse+hidusb svn path=/branches/usb-bringup/; revision=54834 --- drivers/hid/hidparse/CMakeLists.txt | 7 +- drivers/hid/hidparse/hidparse.c | 671 ++++++------- drivers/hid/hidparse/hidparse.h | 2 + lib/drivers/CMakeLists.txt | 1 + lib/drivers/hidparser/CMakeLists.txt | 13 + lib/drivers/hidparser/api.c | 953 ++++++++++++++++++ lib/drivers/hidparser/hidparser.c | 1083 +++++++++++++++++++++ lib/drivers/hidparser/hidparser.h | 427 ++++++++ lib/drivers/hidparser/parser.c | 1347 ++++++++++++++++++++++++++ lib/drivers/hidparser/parser.h | 362 +++++++ 10 files changed, 4490 insertions(+), 376 deletions(-) create mode 100644 lib/drivers/hidparser/CMakeLists.txt create mode 100644 lib/drivers/hidparser/api.c create mode 100644 lib/drivers/hidparser/hidparser.c create mode 100644 lib/drivers/hidparser/hidparser.h create mode 100644 lib/drivers/hidparser/parser.c create mode 100644 lib/drivers/hidparser/parser.h diff --git a/drivers/hid/hidparse/CMakeLists.txt b/drivers/hid/hidparse/CMakeLists.txt index a6740cc646f..14a1f87e1b0 100644 --- a/drivers/hid/hidparse/CMakeLists.txt +++ b/drivers/hid/hidparse/CMakeLists.txt @@ -2,7 +2,10 @@ spec2def(hidparse.sys hidparse.spec) add_definitions(-DDEBUG_MODE) -include_directories(${REACTOS_SOURCE_DIR}/ntoskrnl/include) +include_directories( + ${REACTOS_SOURCE_DIR}/ntoskrnl/include + ${REACTOS_SOURCE_DIR}/lib/drivers/hidparser) + add_library(hidparse SHARED hidparse.c hidparse.rc ${CMAKE_CURRENT_BINARY_DIR}/hidparse.def) @@ -12,3 +15,5 @@ add_cab_target(hidparse 2) add_cab_target(hidparse 2) add_importlib_target(hidparse.spec) + +target_link_libraries(hidparse hidparser) diff --git a/drivers/hid/hidparse/hidparse.c b/drivers/hid/hidparse/hidparse.c index fea2650d973..265cbcd10ce 100644 --- a/drivers/hid/hidparse/hidparse.c +++ b/drivers/hid/hidparse/hidparse.c @@ -10,27 +10,315 @@ #include "hidparse.h" +PVOID +NTAPI +AllocFunction( + IN ULONG ItemSize) +{ + PVOID Item = ExAllocatePool(NonPagedPool, ItemSize); + if (Item) + { + // + // zero item + // + RtlZeroMemory(Item, ItemSize); + } + + // + // done + // + return Item; +} + +VOID +NTAPI +FreeFunction( + IN PVOID Item) +{ + // + // free item + // + ExFreePool(Item); +} + +VOID +NTAPI +ZeroFunction( + IN PVOID Item, + IN ULONG ItemSize) +{ + // + // zero item + // + RtlZeroMemory(Item, ItemSize); +} + +VOID +NTAPI +CopyFunction( + IN PVOID Target, + IN PVOID Source, + IN ULONG Length) +{ + // + // copy item + // + RtlCopyMemory(Target, Source, Length); +} + +VOID +NTAPI +DebugFunction( + IN LPCSTR FormatStr, ...) +{ + + va_list args; + unsigned int i; + char printbuffer[1024]; + + va_start(args, FormatStr); + i = vsprintf(printbuffer, FormatStr, args); + va_end(args); + + DbgPrint(printbuffer); +} VOID NTAPI HidP_FreeCollectionDescription ( IN PHIDP_DEVICE_DESC DeviceDescription) { - DPRINT1("HidP_FreeCollectionDescription DeviceDescription %p\n", DeviceDescription); + HID_PARSER Parser; + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, NULL, &Parser); // // free collection // - ExFreePool(DeviceDescription->CollectionDesc); - - // - // free report ids - // - ExFreePool(DeviceDescription->ReportIDs); + HidParser_FreeCollectionDescription(&Parser, DeviceDescription); } + +HIDAPI +NTSTATUS +NTAPI +HidP_GetCaps( + IN PHIDP_PREPARSED_DATA PreparsedData, + OUT PHIDP_CAPS Capabilities) +{ + HID_PARSER Parser; + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, PreparsedData, &Parser); + + // + // get caps + // + return HidParser_GetCaps(&Parser, Capabilities); +} + +NTSTATUS +NTAPI +HidP_GetCollectionDescription( + IN PHIDP_REPORT_DESCRIPTOR ReportDesc, + IN ULONG DescLength, + IN POOL_TYPE PoolType, + OUT PHIDP_DEVICE_DESC DeviceDescription) +{ + PHID_PARSER Parser; + HIDPARSER_STATUS Status; + + // + // first allocate the parser + // + Status = HidParser_AllocateParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, &Parser); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // not enough memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get description; + // + Status = HidParser_GetCollectionDescription(Parser, ReportDesc, DescLength, PoolType, DeviceDescription); + + // + // FIXME parser memory leak + // + return Status; +} + +HIDAPI +ULONG +NTAPI +HidP_MaxUsageListLength( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage OPTIONAL, + IN PHIDP_PREPARSED_DATA PreparsedData) +{ + HID_PARSER Parser; + + // + // sanity check + // + ASSERT(ReportType == HidP_Input || ReportType == HidP_Output || ReportType == HidP_Feature); + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, PreparsedData, &Parser); + + + // + // get usage length + // + return HidParser_MaxUsageListLength(&Parser, ReportType, UsagePage); +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetSpecificValueCaps( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PHIDP_VALUE_CAPS ValueCaps, + IN OUT PULONG ValueCapsLength, + IN PHIDP_PREPARSED_DATA PreparsedData) +{ + HID_PARSER Parser; + + // + // sanity check + // + ASSERT(ReportType == HidP_Input || ReportType == HidP_Output || ReportType == HidP_Feature); + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, PreparsedData, &Parser); + + // + // get value caps + // + return HidParser_GetSpecificValueCaps(&Parser, ReportType, UsagePage, LinkCollection, Usage, ValueCaps, ValueCapsLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetUsages( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + OUT USAGE *UsageList, + IN OUT ULONG *UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + HID_PARSER Parser; + + // + // sanity check + // + ASSERT(ReportType == HidP_Input || ReportType == HidP_Output || ReportType == HidP_Feature); + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, PreparsedData, &Parser); + + // + // get usages + // + return HidParser_GetUsages(&Parser, ReportType, UsagePage, LinkCollection, UsageList, UsageLength, Report, ReportLength); +} + + #undef HidP_GetButtonCaps +HIDAPI +NTSTATUS +NTAPI +HidP_UsageListDifference( + IN PUSAGE PreviousUsageList, + IN PUSAGE CurrentUsageList, + OUT PUSAGE BreakUsageList, + OUT PUSAGE MakeUsageList, + IN ULONG UsageListLength) +{ + return HidParser_UsageListDifference(PreviousUsageList, CurrentUsageList, BreakUsageList, MakeUsageList, UsageListLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetUsagesEx( + IN HIDP_REPORT_TYPE ReportType, + IN USHORT LinkCollection, + OUT PUSAGE_AND_PAGE ButtonList, + IN OUT ULONG *UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + return HidP_GetUsages(ReportType, HID_USAGE_PAGE_UNDEFINED, LinkCollection, (PUSAGE)ButtonList, UsageLength, PreparsedData, Report, ReportLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidP_UsageAndPageListDifference( + IN PUSAGE_AND_PAGE PreviousUsageList, + IN PUSAGE_AND_PAGE CurrentUsageList, + OUT PUSAGE_AND_PAGE BreakUsageList, + OUT PUSAGE_AND_PAGE MakeUsageList, + IN ULONG UsageListLength) +{ + return HidParser_UsageAndPageListDifference(PreviousUsageList, CurrentUsageList, BreakUsageList, MakeUsageList, UsageListLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetScaledUsageValue( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + OUT PLONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + HID_PARSER Parser; + + // + // sanity check + // + ASSERT(ReportType == HidP_Input || ReportType == HidP_Output || ReportType == HidP_Feature); + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, PreparsedData, &Parser); + + // + // get scaled usage value + // + return HidParser_GetScaledUsageValue(&Parser, ReportType, UsagePage, LinkCollection, Usage, UsageValue, Report, ReportLength); +} + HIDAPI NTSTATUS NTAPI @@ -60,32 +348,6 @@ HidP_GetSpecificButtonCaps( return STATUS_NOT_IMPLEMENTED; } - -HIDAPI -NTSTATUS -NTAPI -HidP_GetCaps( - IN PHIDP_PREPARSED_DATA PreparsedData, - OUT PHIDP_CAPS Capabilities) -{ - UNIMPLEMENTED - ASSERT(FALSE); - return STATUS_NOT_IMPLEMENTED; -} - -NTSTATUS -NTAPI -HidP_GetCollectionDescription( - IN PHIDP_REPORT_DESCRIPTOR ReportDesc, - IN ULONG DescLength, - IN POOL_TYPE PoolType, - OUT PHIDP_DEVICE_DESC DeviceDescription) -{ - UNIMPLEMENTED - ASSERT(FALSE); - return STATUS_NOT_IMPLEMENTED; -} - HIDAPI NTSTATUS NTAPI @@ -107,7 +369,7 @@ NTSTATUS NTAPI HidP_GetExtendedAttributes( IN HIDP_REPORT_TYPE ReportType, - IN USHORT DataIndex, + IN USAGE UsagePage, IN PHIDP_PREPARSED_DATA PreparsedData, OUT PHIDP_EXTENDED_ATTRIBUTES Attributes, IN OUT PULONG LengthAttributes) @@ -130,24 +392,6 @@ HidP_GetLinkCollectionNodes( return STATUS_NOT_IMPLEMENTED; } -HIDAPI -NTSTATUS -NTAPI -HidP_GetScaledUsageValue( - IN HIDP_REPORT_TYPE ReportType, - IN USAGE UsagePage, - IN USHORT LinkCollection OPTIONAL, - IN USAGE Usage, - OUT PLONG UsageValue, - IN PHIDP_PREPARSED_DATA PreparsedData, - IN PCHAR Report, - IN ULONG ReportLength) -{ - UNIMPLEMENTED - ASSERT(FALSE); - return STATUS_NOT_IMPLEMENTED; -} - HIDAPI NTSTATUS NTAPI @@ -166,185 +410,6 @@ HidP_GetUsageValue( return STATUS_NOT_IMPLEMENTED; } - - -HIDAPI -NTSTATUS -NTAPI -HidP_UsageListDifference( - IN PUSAGE PreviousUsageList, - IN PUSAGE CurrentUsageList, - OUT PUSAGE BreakUsageList, - OUT PUSAGE MakeUsageList, - IN ULONG UsageListLength) -{ - ULONG Index, SubIndex, bFound, BreakUsageIndex = 0, MakeUsageIndex = 0; - USAGE CurrentUsage, Usage; - - if (UsageListLength) - { - Index = 0; - do - { - /* get current usage */ - CurrentUsage = PreviousUsageList[Index]; - - /* is the end of list reached? */ - if (!CurrentUsage) - break; - - /* start searching in current usage list */ - SubIndex = 0; - bFound = FALSE; - do - { - /* get usage of current list */ - Usage = CurrentUsageList[SubIndex]; - - /* end of list reached? */ - if (!Usage) - break; - - /* check if it matches the current one */ - if (CurrentUsage == Usage) - { - /* it does */ - bFound = TRUE; - break; - } - - /* move to next usage */ - SubIndex++; - }while(SubIndex < UsageListLength); - - /* was the usage found ?*/ - if (!bFound) - { - /* store it in the break usage list */ - BreakUsageList[BreakUsageIndex] = CurrentUsage; - BreakUsageIndex++; - } - - /* move to next usage */ - Index++; - - }while(Index < UsageListLength); - - /* now process the new items */ - Index = 0; - do - { - /* get current usage */ - CurrentUsage = CurrentUsageList[Index]; - - /* is the end of list reached? */ - if (!CurrentUsage) - break; - - /* start searching in current usage list */ - SubIndex = 0; - bFound = FALSE; - do - { - /* get usage of previous list */ - Usage = PreviousUsageList[SubIndex]; - - /* end of list reached? */ - if (!Usage) - break; - - /* check if it matches the current one */ - if (CurrentUsage == Usage) - { - /* it does */ - bFound = TRUE; - break; - } - - /* move to next usage */ - SubIndex++; - }while(SubIndex < UsageListLength); - - /* was the usage found ?*/ - if (!bFound) - { - /* store it in the make usage list */ - MakeUsageList[MakeUsageIndex] = CurrentUsage; - MakeUsageIndex++; - } - - /* move to next usage */ - Index++; - - }while(Index < UsageListLength); - } - - /* does the break list contain empty entries */ - if (BreakUsageIndex < UsageListLength) - { - /* zeroize entries */ - RtlZeroMemory(&BreakUsageList[BreakUsageIndex], sizeof(USAGE) * (UsageListLength - BreakUsageIndex)); - } - - /* does the make usage list contain empty entries */ - if (MakeUsageIndex < UsageListLength) - { - /* zeroize entries */ - RtlZeroMemory(&MakeUsageList[MakeUsageIndex], sizeof(USAGE) * (UsageListLength - MakeUsageIndex)); - } - - /* done */ - return HIDP_STATUS_SUCCESS; -} - -HIDAPI -NTSTATUS -NTAPI -HidP_GetSpecificValueCaps( - IN HIDP_REPORT_TYPE ReportType, - IN USAGE UsagePage, - IN USHORT LinkCollection, - IN USAGE Usage, - OUT PHIDP_VALUE_CAPS ValueCaps, - IN OUT PULONG ValueCapsLength, - IN PHIDP_PREPARSED_DATA PreparsedData) -{ - UNIMPLEMENTED - ASSERT(FALSE); - return STATUS_NOT_IMPLEMENTED; -} - -HIDAPI -ULONG -NTAPI -HidP_MaxUsageListLength( - IN HIDP_REPORT_TYPE ReportType, - IN USAGE UsagePage OPTIONAL, - IN PHIDP_PREPARSED_DATA PreparsedData) -{ - UNIMPLEMENTED - ASSERT(FALSE); - return STATUS_NOT_IMPLEMENTED; -} - -HIDAPI -NTSTATUS -NTAPI -HidP_GetUsages( - IN HIDP_REPORT_TYPE ReportType, - IN USAGE UsagePage, - IN USHORT LinkCollection OPTIONAL, - OUT USAGE *UsageList, - IN OUT ULONG *UsageLength, - IN PHIDP_PREPARSED_DATA PreparsedData, - IN PCHAR Report, - IN ULONG ReportLength) -{ - UNIMPLEMENTED - ASSERT(FALSE); - return STATUS_NOT_IMPLEMENTED; -} - NTSTATUS NTAPI HidP_SysPowerEvent ( @@ -388,150 +453,6 @@ HidP_GetUsageValueArray( return STATUS_NOT_IMPLEMENTED; } -HIDAPI -NTSTATUS -NTAPI -HidP_GetUsagesEx( - IN HIDP_REPORT_TYPE ReportType, - IN USHORT LinkCollection, - OUT PUSAGE_AND_PAGE ButtonList, - IN OUT ULONG *UsageLength, - IN PHIDP_PREPARSED_DATA PreparsedData, - IN PCHAR Report, - IN ULONG ReportLength) -{ - return HidP_GetUsages(ReportType, HID_USAGE_PAGE_UNDEFINED, LinkCollection, (PUSAGE)ButtonList, UsageLength, PreparsedData, Report, ReportLength); -} - - -HIDAPI -NTSTATUS -NTAPI -HidP_UsageAndPageListDifference( - IN PUSAGE_AND_PAGE PreviousUsageList, - IN PUSAGE_AND_PAGE CurrentUsageList, - OUT PUSAGE_AND_PAGE BreakUsageList, - OUT PUSAGE_AND_PAGE MakeUsageList, - IN ULONG UsageListLength) -{ - ULONG Index, SubIndex, BreakUsageListIndex = 0, MakeUsageListIndex = 0, bFound; - PUSAGE_AND_PAGE CurrentUsage, Usage; - - if (UsageListLength) - { - /* process removed usages */ - Index = 0; - do - { - /* get usage from current index */ - CurrentUsage = &PreviousUsageList[Index]; - - /* end of list reached? */ - if (CurrentUsage->Usage == 0 && CurrentUsage->UsagePage == 0) - break; - - /* search in current list */ - SubIndex = 0; - bFound = FALSE; - do - { - /* get usage */ - Usage = &CurrentUsageList[SubIndex]; - - /* end of list reached? */ - if (Usage->Usage == 0 && Usage->UsagePage == 0) - break; - - /* does it match */ - if (Usage->Usage == CurrentUsage->Usage && Usage->UsagePage == CurrentUsage->UsagePage) - { - /* found match */ - bFound = TRUE; - } - - /* move to next index */ - SubIndex++; - - }while(SubIndex < UsageListLength); - - if (!bFound) - { - /* store it in break usage list */ - BreakUsageList[BreakUsageListIndex].Usage = CurrentUsage->Usage; - BreakUsageList[BreakUsageListIndex].UsagePage = CurrentUsage->UsagePage; - BreakUsageListIndex++; - } - - /* move to next index */ - Index++; - - }while(Index < UsageListLength); - - /* process new usages */ - Index = 0; - do - { - /* get usage from current index */ - CurrentUsage = &CurrentUsageList[Index]; - - /* end of list reached? */ - if (CurrentUsage->Usage == 0 && CurrentUsage->UsagePage == 0) - break; - - /* search in current list */ - SubIndex = 0; - bFound = FALSE; - do - { - /* get usage */ - Usage = &PreviousUsageList[SubIndex]; - - /* end of list reached? */ - if (Usage->Usage == 0 && Usage->UsagePage == 0) - break; - - /* does it match */ - if (Usage->Usage == CurrentUsage->Usage && Usage->UsagePage == CurrentUsage->UsagePage) - { - /* found match */ - bFound = TRUE; - } - - /* move to next index */ - SubIndex++; - - }while(SubIndex < UsageListLength); - - if (!bFound) - { - /* store it in break usage list */ - MakeUsageList[MakeUsageListIndex].Usage = CurrentUsage->Usage; - MakeUsageList[MakeUsageListIndex].UsagePage = CurrentUsage->UsagePage; - MakeUsageListIndex++; - } - - /* move to next index */ - Index++; - }while(Index < UsageListLength); - } - - /* are there remaining free list entries */ - if (BreakUsageListIndex < UsageListLength) - { - /* zero them */ - RtlZeroMemory(&BreakUsageList[BreakUsageListIndex], (UsageListLength - BreakUsageListIndex) * sizeof(USAGE_AND_PAGE)); - } - - /* are there remaining free list entries */ - if (MakeUsageListIndex < UsageListLength) - { - /* zero them */ - RtlZeroMemory(&MakeUsageList[MakeUsageListIndex], (UsageListLength - MakeUsageListIndex) * sizeof(USAGE_AND_PAGE)); - } - - /* done */ - return HIDP_STATUS_SUCCESS; -} HIDAPI NTSTATUS diff --git a/drivers/hid/hidparse/hidparse.h b/drivers/hid/hidparse/hidparse.h index f0bc0d2c49b..fa516eb3eff 100644 --- a/drivers/hid/hidparse/hidparse.h +++ b/drivers/hid/hidparse/hidparse.h @@ -5,4 +5,6 @@ #include #include #include +#define NDEBUG #include +#include "hidparser.h" \ No newline at end of file diff --git a/lib/drivers/CMakeLists.txt b/lib/drivers/CMakeLists.txt index 72024e812df..f49a39ffe58 100644 --- a/lib/drivers/CMakeLists.txt +++ b/lib/drivers/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(chew) add_subdirectory(csq) +add_subdirectory(hidparser) add_subdirectory(ip) add_subdirectory(oskittcp) add_subdirectory(sound) diff --git a/lib/drivers/hidparser/CMakeLists.txt b/lib/drivers/hidparser/CMakeLists.txt new file mode 100644 index 00000000000..5d16301ce1e --- /dev/null +++ b/lib/drivers/hidparser/CMakeLists.txt @@ -0,0 +1,13 @@ + +add_definitions( + -DUNICODE -D_UNICODE + -DNDEBUG=1) + +list(APPEND SOURCE + hidparser.c + parser.c + api.c) + +add_library(hidparser ${SOURCE}) +add_dependencies(hidparser bugcodes) + diff --git a/lib/drivers/hidparser/api.c b/lib/drivers/hidparser/api.c new file mode 100644 index 00000000000..45765f07d1a --- /dev/null +++ b/lib/drivers/hidparser/api.c @@ -0,0 +1,953 @@ +/* + * PROJECT: ReactOS HID Parser Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/hidparser/api.c + * PURPOSE: HID Parser + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + + +#include "parser.h" + + +ULONG +HidParser_NumberOfTopCollections( + IN PHID_PARSER Parser) +{ + PHID_PARSER_CONTEXT ParserContext; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity checks + // + ASSERT(ParserContext); + ASSERT(ParserContext->RootCollection); + ASSERT(ParserContext->RootCollection->NodeCount); + + // + // number of top collections + // + return ParserContext->RootCollection->NodeCount; +} + +PHID_COLLECTION +HidParser_GetCollection( + IN PHID_PARSER Parser, + IN ULONG CollectionNumber) +{ + PHID_PARSER_CONTEXT ParserContext; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity checks + // + ASSERT(ParserContext); + ASSERT(ParserContext->RootCollection); + ASSERT(ParserContext->RootCollection->NodeCount); + + // + // is collection index out of bounds + // + if (CollectionNumber < ParserContext->RootCollection->NodeCount) + { + // + // valid collection + // + return ParserContext->RootCollection->Nodes[CollectionNumber]; + } + + // + // no such collection + // + Parser->Debug("HIDPARSE] No such collection %lu\n", CollectionNumber); + return NULL; +} + +PHID_REPORT +HidParser_GetReportByType( + IN PHID_PARSER Parser, + IN ULONG ReportType) +{ + PHID_PARSER_CONTEXT ParserContext; + ULONG Index; + ULONG ReportCount = 0; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity checks + // + ASSERT(ParserContext); + + // + // FIXME support multiple top collecions + // + ASSERT(ParserContext->RootCollection->NodeCount == 1); + for(Index = 0; Index < ParserContext->ReportCount; Index++) + { + // + // check if the report type match + // + if (ParserContext->Reports[Index]->Type == ReportType) + { + // + // found report + // + return ParserContext->Reports[Index]; + } + } + + // + // report not found + // + return NULL; +} + + +ULONG +HidParser_NumberOfReports( + IN PHID_PARSER Parser, + IN ULONG ReportType) +{ + PHID_PARSER_CONTEXT ParserContext; + ULONG Index; + ULONG ReportCount = 0; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity checks + // + ASSERT(ParserContext); + + // + // FIXME support multiple top collecions + // + ASSERT(ParserContext->RootCollection->NodeCount == 1); + for(Index = 0; Index < ParserContext->ReportCount; Index++) + { + // + // check if the report type match + // + if (ParserContext->Reports[Index]->Type == ReportType) + { + // + // found report + // + ReportCount++; + } + } + + // + // done + // + return ReportCount; +} + +HIDPARSER_STATUS +HidParser_GetCollectionUsagePage( + IN PHID_PARSER Parser, + IN ULONG CollectionIndex, + OUT PUSHORT Usage, + OUT PUSHORT UsagePage) +{ + PHID_COLLECTION Collection; + + // + // find collection + // + Collection = HidParser_GetCollection(Parser, CollectionIndex); + if (!Collection) + { + // + // collection not found + // + return HIDPARSER_STATUS_COLLECTION_NOT_FOUND; + } + + // + // store result + // + *UsagePage = (Collection->Usage >> 16); + *Usage = (Collection->Usage & 0xFFFF); + return HIDPARSER_STATUS_SUCCESS; +} + +ULONG +HidParser_GetReportLength( + IN PHID_PARSER Parser, + IN ULONG ReportType) +{ + PHID_PARSER_CONTEXT ParserContext; + PHID_REPORT Report; + ULONG ReportLength; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity checks + // + ASSERT(ParserContext); + + // + // FIXME support multiple top collecions + // + ASSERT(ParserContext->RootCollection->NodeCount == 1); + + // + // get first report + // + Report = HidParser_GetReportByType(Parser, ReportType); + if (!Report) + { + // + // no report found + // + return 0; + } + + // + // get report length + // + ReportLength = Report->ReportSize; + + // + // done + // + if (ReportLength) + { + // + // byte aligned length + // + ASSERT(ReportLength % 8 == 0); + return ReportLength / 8; + } + return ReportLength; +} + +UCHAR +HidParser_IsReportIDUsed( + IN PHID_PARSER Parser) +{ + PHID_PARSER_CONTEXT ParserContext; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity checks + // + ASSERT(ParserContext); + + // + // return flag + // + return ParserContext->UseReportIDs; +} + +ULONG +HidParser_GetReportItemCountFromReportType( + IN PHID_PARSER Parser, + IN ULONG ReportType) +{ + PHID_PARSER_CONTEXT ParserContext; + PHID_REPORT Report; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity checks + // + ASSERT(ParserContext); + + // + // FIXME support multiple top collecions + // + ASSERT(ParserContext->RootCollection->NodeCount == 1); + + // + // get report + // + Report = HidParser_GetReportByType(Parser, ReportType); + if (!Report) + { + // + // no such report + // + return 0; + } + + // + // return report item count + // + return Report->ItemCount; +} + + +ULONG +HidParser_GetReportItemTypeCountFromReportType( + IN PHID_PARSER Parser, + IN ULONG ReportType, + IN ULONG bData) +{ + PHID_PARSER_CONTEXT ParserContext; + ULONG Index; + PHID_REPORT Report; + ULONG ItemCount = 0; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity checks + // + ASSERT(ParserContext); + + // + // FIXME support multiple top collecions + // + ASSERT(ParserContext->RootCollection->NodeCount == 1); + + // + // get report + // + Report = HidParser_GetReportByType(Parser, ReportType); + if (!Report) + { + // + // no such report + // + return 0; + } + + // + // enumerate all items + // + for(Index = 0; Index < Report->ItemCount; Index++) + { + // + // check item type + // + if (Report->Items[Index]->HasData && bData == TRUE) + { + // + // found data item + // + ItemCount++; + } + else if (Report->Items[Index]->HasData == FALSE && bData == FALSE) + { + // + // found value item + // + ItemCount++; + } + } + + // + // no report items + // + return ItemCount; +} + +ULONG +HidParser_GetContextSize( + IN PHID_PARSER Parser) +{ + // + // FIXME the context must contain all parsed info + // + return sizeof(HID_PARSER_CONTEXT); +} + +VOID +HidParser_FreeContext( + IN PHID_PARSER Parser, + IN PUCHAR Context, + IN ULONG ContextLength) +{ + // + // FIXME implement freeing of parsed info + // +} + +HIDPARSER_STATUS +HidParser_AllocateParser( + IN PHIDPARSER_ALLOC_FUNCTION AllocFunction, + IN PHIDPARSER_FREE_FUNCTION FreeFunction, + IN PHIDPARSER_ZERO_FUNCTION ZeroFunction, + IN PHIDPARSER_COPY_FUNCTION CopyFunction, + IN PHIDPARSER_DEBUG_FUNCTION DebugFunction, + OUT PHID_PARSER *OutParser) +{ + PHID_PARSER Parser; + PHID_PARSER_CONTEXT ParserContext; + + // + // allocate + // + Parser = (PHID_PARSER)AllocFunction(sizeof(HID_PARSER)); + if (!Parser) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // allocate parser context + // + ParserContext = (PHID_PARSER_CONTEXT)AllocFunction(sizeof(HID_PARSER_CONTEXT)); + if (!ParserContext) + { + // + // no memory + // + FreeFunction(Parser); + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // init parser + // + Parser->Alloc = AllocFunction; + Parser->Free = FreeFunction; + Parser->Zero = ZeroFunction; + Parser->Copy = CopyFunction; + Parser->Debug = DebugFunction; + Parser->ParserContext = ParserContext; + + // + // store result + // + *OutParser = Parser; + // + // success + // + return HIDPARSER_STATUS_SUCCESS; +} + +VOID +HidParser_InitParser( + IN PHIDPARSER_ALLOC_FUNCTION AllocFunction, + IN PHIDPARSER_FREE_FUNCTION FreeFunction, + IN PHIDPARSER_ZERO_FUNCTION ZeroFunction, + IN PHIDPARSER_COPY_FUNCTION CopyFunction, + IN PHIDPARSER_DEBUG_FUNCTION DebugFunction, + IN PVOID ParserContext, + OUT PHID_PARSER Parser) +{ + Parser->Alloc = AllocFunction; + Parser->Free = FreeFunction; + Parser->Zero = ZeroFunction; + Parser->Copy = CopyFunction; + Parser->Debug = DebugFunction; + Parser->ParserContext = ParserContext; +} + +ULONG +HidParser_GetCollectionCount( + IN PHID_COLLECTION Collection) +{ + ULONG Index; + ULONG Count = Collection->NodeCount; + + for(Index = 0; Index < Collection->NodeCount; Index++) + { + // + // count collection for sub nodes + // + Count += HidParser_GetCollectionCount(Collection->Nodes[Index]); + } + + // + // done + // + return Count; +} + +ULONG +HidParser_GetTotalCollectionCount( + IN PHID_PARSER Parser) +{ + PHID_PARSER_CONTEXT ParserContext; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity check + // + ASSERT(ParserContext); + ASSERT(ParserContext->RootCollection); + + // + // count collections + // + return HidParser_GetCollectionCount(ParserContext->RootCollection); +} + +ULONG +HidParser_GetMaxUsageListLengthWithReportAndPage( + IN PHID_PARSER Parser, + IN ULONG ReportType, + IN USAGE UsagePage OPTIONAL) +{ + PHID_PARSER_CONTEXT ParserContext; + ULONG Index; + PHID_REPORT Report; + ULONG ItemCount = 0; + USHORT CurrentUsagePage; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity checks + // + ASSERT(ParserContext); + + // + // FIXME support multiple top collecions + // + ASSERT(ParserContext->RootCollection->NodeCount == 1); + + // + // get report + // + Report = HidParser_GetReportByType(Parser, ReportType); + if (!Report) + { + // + // no such report + // + return 0; + } + + for(Index = 0; Index < Report->ItemCount; Index++) + { + // + // check usage page + // + CurrentUsagePage = (Report->Items[Index]->UsageMinimum >> 16); + if (CurrentUsagePage == UsagePage && Report->Items[Index]->HasData) + { + // + // found item + // + ItemCount++; + } + } + + // + // done + // + return ItemCount; +} + +HIDPARSER_STATUS +HidParser_GetSpecificValueCapsWithReport( + IN PHID_PARSER Parser, + IN ULONG ReportType, + IN USHORT UsagePage, + IN USHORT Usage, + OUT PHIDP_VALUE_CAPS ValueCaps, + IN OUT PULONG ValueCapsLength) +{ + PHID_PARSER_CONTEXT ParserContext; + ULONG Index; + PHID_REPORT Report; + ULONG ItemCount = 0; + USHORT CurrentUsagePage; + USHORT CurrentUsage; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity checks + // + ASSERT(ParserContext); + + // + // FIXME support multiple top collecions + // + ASSERT(ParserContext->RootCollection->NodeCount == 1); + + // + // get report + // + Report = HidParser_GetReportByType(Parser, ReportType); + if (!Report) + { + // + // no such report + // + return HIDPARSER_STATUS_REPORT_NOT_FOUND; + } + + for(Index = 0; Index < Report->ItemCount; Index++) + { + // + // check usage page + // + CurrentUsagePage = (Report->Items[Index]->UsageMinimum >> 16); + CurrentUsage = (Report->Items[Index]->UsageMinimum & 0xFFFF); + + if ((Usage == CurrentUsage && UsagePage == CurrentUsagePage) || (Usage == 0 && UsagePage == CurrentUsagePage) || (Usage == CurrentUsage && UsagePage == 0) || (Usage == 0 && UsagePage == 0)) + { + // + // check if there is enough place for the caps + // + if (ItemCount < *ValueCapsLength) + { + // + // zero caps + // + Parser->Zero(&ValueCaps[ItemCount], sizeof(HIDP_VALUE_CAPS)); + + // + // init caps + // + ValueCaps[ItemCount].UsagePage = CurrentUsagePage; + ValueCaps[ItemCount].ReportID = Report->ReportID; + ValueCaps[ItemCount].LogicalMin = Report->Items[Index]->Minimum; + ValueCaps[ItemCount].LogicalMax = Report->Items[Index]->Maximum; + ValueCaps[ItemCount].IsAbsolute = !Report->Items[Index]->Relative; + ValueCaps[ItemCount].BitSize = Report->Items[Index]->BitCount; + + // + // FIXME: FILLMEIN + // + } + + + // + // found item + // + ItemCount++; + } + } + + // + // store result + // + *ValueCapsLength = ItemCount; + + if (ItemCount) + { + // + // success + // + return HIDPARSER_STATUS_SUCCESS; + } + + // + // item not found + // + return HIDPARSER_STATUS_USAGE_NOT_FOUND; +} + +HIDPARSER_STATUS +HidParser_GetUsagesWithReport( + IN PHID_PARSER Parser, + IN ULONG ReportType, + IN USAGE UsagePage, + OUT USAGE *UsageList, + IN OUT PULONG UsageLength, + IN PCHAR ReportDescriptor, + IN ULONG ReportDescriptorLength) +{ + PHID_PARSER_CONTEXT ParserContext; + ULONG Index; + PHID_REPORT Report; + ULONG ItemCount = 0; + USHORT CurrentUsagePage; + PHID_REPORT_ITEM ReportItem; + UCHAR Activated; + ULONG Data; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity checks + // + ASSERT(ParserContext); + + // + // FIXME support multiple top collecions + // + ASSERT(ParserContext->RootCollection->NodeCount == 1); + + // + // get report + // + Report = HidParser_GetReportByType(Parser, ReportType); + if (!Report) + { + // + // no such report + // + return HIDPARSER_STATUS_REPORT_NOT_FOUND; + } + + if (Report->ReportSize / 8 != (ReportDescriptorLength - 1)) + { + // + // invalid report descriptor length + // + return HIDPARSER_STATUS_INVALID_REPORT_LENGTH; + } + + for(Index = 0; Index < Report->ItemCount; Index++) + { + // + // get report item + // + ReportItem = Report->Items[Index]; + + // + // does it have data + // + if (!ReportItem->HasData) + continue; + + // + // check usage page + // + CurrentUsagePage = (ReportItem->UsageMinimum >> 16); + + // + // does usage match + // + if (UsagePage != CurrentUsagePage) + continue; + + // + // check if the specified usage is activated + // + ASSERT(ReportItem->ByteOffset < ReportDescriptorLength); + ASSERT(ReportItem->BitCount < 8); + + // + // one extra shift for skipping the prepended report id + // + Data = ReportDescriptor[ReportItem->ByteOffset + 1]; + + // + // shift data + // + Data >>= ReportItem->Shift; + + // + // clear unwanted bits + // + Data &= ReportItem->Mask; + + // + // is it activated + // + Activated = (Data != 0); + + if (!Activated) + continue; + + // + // is there enough space for the usage + // + if (ItemCount >= *UsageLength) + { + ItemCount++; + continue; + } + + // + // store item + // + UsageList[ItemCount] = (ReportItem->UsageMinimum & 0xFFFF); + ItemCount++; + } + + if (ItemCount > *UsageLength) + { + // + // list too small + // + return HIDPARSER_STATUS_BUFFER_TOO_SMALL; + } + + // + // success, clear rest of array + // + Parser->Zero(&UsageList[ItemCount], (*UsageLength - ItemCount) * sizeof(USAGE)); + + // + // store result size + // + *UsageLength = ItemCount; + + // + // done + // + return HIDPARSER_STATUS_SUCCESS; +} + +HIDPARSER_STATUS +HidParser_GetScaledUsageValueWithReport( + IN PHID_PARSER Parser, + IN ULONG ReportType, + IN USAGE UsagePage, + IN USAGE Usage, + OUT PLONG UsageValue, + IN PCHAR ReportDescriptor, + IN ULONG ReportDescriptorLength) +{ + PHID_PARSER_CONTEXT ParserContext; + ULONG Index; + PHID_REPORT Report; + ULONG ItemCount = 0; + USHORT CurrentUsagePage; + PHID_REPORT_ITEM ReportItem; + ULONG Data; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + // + // sanity checks + // + ASSERT(ParserContext); + + // + // FIXME support multiple top collecions + // + ASSERT(ParserContext->RootCollection->NodeCount == 1); + + // + // get report + // + Report = HidParser_GetReportByType(Parser, ReportType); + if (!Report) + { + // + // no such report + // + return HIDPARSER_STATUS_REPORT_NOT_FOUND; + } + + if (Report->ReportSize / 8 != (ReportDescriptorLength - 1)) + { + // + // invalid report descriptor length + // + return HIDPARSER_STATUS_INVALID_REPORT_LENGTH; + } + + for(Index = 0; Index < Report->ItemCount; Index++) + { + // + // get report item + // + ReportItem = Report->Items[Index]; + + // + // check usage page + // + CurrentUsagePage = (ReportItem->UsageMinimum >> 16); + + // + // does usage page match + // + if (UsagePage != CurrentUsagePage) + continue; + + // + // does the usage match + // + if (Usage != (ReportItem->UsageMinimum & 0xFFFF)) + continue; + + // + // check if the specified usage is activated + // + ASSERT(ReportItem->ByteOffset < ReportDescriptorLength); + + // + // one extra shift for skipping the prepended report id + // + Data = 0; + Parser->Copy(&Data, &ReportDescriptor[ReportItem->ByteOffset +1], min(sizeof(ULONG), ReportDescriptorLength - (ReportItem->ByteOffset + 1))); + Data = ReportDescriptor[ReportItem->ByteOffset + 1]; + + // + // shift data + // + Data >>= ReportItem->Shift; + + // + // clear unwanted bits + // + Data &= ReportItem->Mask; + + if (ReportItem->Minimum > ReportItem->Maximum) + { + // + // logical boundaries are signed values + // + if ((Data & ~(ReportItem->Mask >> 1)) != 0) + { + Data |= ~ReportItem->Mask; + } + } + + // + // store result + // + *UsageValue = Data; + return HIDPARSER_STATUS_SUCCESS; + } + + // + // usage not found + // + return HIDPARSER_STATUS_USAGE_NOT_FOUND; +} diff --git a/lib/drivers/hidparser/hidparser.c b/lib/drivers/hidparser/hidparser.c new file mode 100644 index 00000000000..ddde2f7fca8 --- /dev/null +++ b/lib/drivers/hidparser/hidparser.c @@ -0,0 +1,1083 @@ +/* + * PROJECT: ReactOS HID Parser Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/hidparser/hidparser.c + * PURPOSE: HID Parser + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "parser.h" + +NTSTATUS +TranslateHidParserStatus( + IN HIDPARSER_STATUS Status) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +HidParser_GetCollectionDescription( + IN PHID_PARSER Parser, + IN PHIDP_REPORT_DESCRIPTOR ReportDesc, + IN ULONG DescLength, + IN POOL_TYPE PoolType, + OUT PHIDP_DEVICE_DESC DeviceDescription) +{ + HIDPARSER_STATUS ParserStatus; + ULONG CollectionCount, ReportCount; + ULONG Index; + + // + // first parse the report descriptor + // + ParserStatus = HidParser_ParseReportDescriptor(Parser, ReportDesc, DescLength); + if (ParserStatus != HIDPARSER_STATUS_SUCCESS) + { + // + // failed to parse report descriptor + // + Parser->Debug("[HIDPARSER] Failed to parse report descriptor with %x\n", ParserStatus); + return TranslateHidParserStatus(ParserStatus); + } + + // + // get collection count + // + CollectionCount = HidParser_NumberOfTopCollections(Parser); + + // + // FIXME: only one top level collection is supported + // + ASSERT(CollectionCount <= 1); + if (CollectionCount == 0) + { + // + // no top level collections found + // + return STATUS_NO_DATA_DETECTED; + } + + // + // zero description + // + Parser->Zero(DeviceDescription, sizeof(HIDP_DEVICE_DESC)); + + // + // allocate collection + // + DeviceDescription->CollectionDesc = (PHIDP_COLLECTION_DESC)Parser->Alloc(sizeof(HIDP_COLLECTION_DESC) * CollectionCount); + if (!DeviceDescription->CollectionDesc) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // allocate report description + // + DeviceDescription->ReportIDs = (PHIDP_REPORT_IDS)Parser->Alloc(sizeof(HIDP_REPORT_IDS) * CollectionCount); + if (!DeviceDescription->ReportIDs) + { + // + // no memory + // + Parser->Free(DeviceDescription->CollectionDesc); + return STATUS_INSUFFICIENT_RESOURCES; + } + + for(Index = 0; Index < CollectionCount; Index++) + { + // + // init report description + // + DeviceDescription->ReportIDs[Index].CollectionNumber = Index + 1; + DeviceDescription->ReportIDs[Index].ReportID = Index; //FIXME + DeviceDescription->ReportIDs[Index].InputLength = HidParser_GetReportLength(Parser, HID_REPORT_TYPE_INPUT); + DeviceDescription->ReportIDs[Index].OutputLength = HidParser_GetReportLength(Parser, HID_REPORT_TYPE_OUTPUT); + DeviceDescription->ReportIDs[Index].FeatureLength = HidParser_GetReportLength(Parser, HID_REPORT_TYPE_FEATURE); + + // + // init collection description + // + DeviceDescription->CollectionDesc[Index].CollectionNumber = Index + 1; + + // + // get collection usage page + // + ParserStatus = HidParser_GetCollectionUsagePage(Parser, Index, &DeviceDescription->CollectionDesc[Index].Usage, &DeviceDescription->CollectionDesc[Index].UsagePage); + + // + // windows seems to prepend the report id, regardless if it is required + // + DeviceDescription->CollectionDesc[Index].InputLength = (DeviceDescription->ReportIDs[Index].InputLength > 0 ? DeviceDescription->ReportIDs[Index].InputLength + 1 : 0); + DeviceDescription->CollectionDesc[Index].OutputLength = (DeviceDescription->ReportIDs[Index].OutputLength > 0 ? DeviceDescription->ReportIDs[Index].OutputLength + 1 : 0); + DeviceDescription->CollectionDesc[Index].FeatureLength = (DeviceDescription->ReportIDs[Index].FeatureLength > 0 ? DeviceDescription->ReportIDs[Index].FeatureLength + 1 : 0); + + // + // set preparsed data length + // + DeviceDescription->CollectionDesc[Index].PreparsedDataLength = HidParser_GetContextSize(Parser); + DeviceDescription->CollectionDesc[Index].PreparsedData = Parser->Alloc(DeviceDescription->CollectionDesc[Index].PreparsedDataLength); + if (!DeviceDescription->CollectionDesc[Index].PreparsedData) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy context + // + Parser->Copy(DeviceDescription->CollectionDesc[Index].PreparsedData, Parser->ParserContext, DeviceDescription->CollectionDesc[Index].PreparsedDataLength); + } + + // + // store collection & report count + // + DeviceDescription->CollectionDescLength = CollectionCount; + DeviceDescription->ReportIDsLength = CollectionCount; + + // + // done + // + return STATUS_SUCCESS; +} + +VOID +NTAPI +HidParser_FreeCollectionDescription( + IN PHID_PARSER Parser, + IN PHIDP_DEVICE_DESC DeviceDescription) +{ + ULONG Index; + + // + // first free all context + // + for(Index = 0; Index < DeviceDescription->CollectionDescLength; Index++) + { + // + // free parser context + // + HidParser_FreeContext(Parser, (PUCHAR)DeviceDescription->CollectionDesc[Index].PreparsedData, DeviceDescription->CollectionDesc[Index].PreparsedDataLength); + } + + // + // now free collection description + // + Parser->Free(DeviceDescription->CollectionDesc); + + // + // free report description + // + ExFreePool(DeviceDescription->ReportIDs); +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetCaps( + IN PHID_PARSER Parser, + OUT PHIDP_CAPS Capabilities) +{ + ULONG CollectionNumber; + // + // zero capabilities + // + Parser->Zero(Capabilities, sizeof(HIDP_CAPS)); + + // + // FIXME support multiple top level collections + // + CollectionNumber = 0; + + // + // init capabilities + // + HidParser_GetCollectionUsagePage(Parser, CollectionNumber, &Capabilities->Usage, &Capabilities->UsagePage); + Capabilities->InputReportByteLength = HidParser_GetReportLength(Parser, HID_REPORT_TYPE_INPUT); + Capabilities->OutputReportByteLength = HidParser_GetReportLength(Parser, HID_REPORT_TYPE_OUTPUT); + Capabilities->FeatureReportByteLength = HidParser_GetReportLength(Parser, HID_REPORT_TYPE_FEATURE); + + // + // always pre-prend report id + // + Capabilities->InputReportByteLength = (Capabilities->InputReportByteLength > 0 ? Capabilities->InputReportByteLength + 1 : 0); + Capabilities->OutputReportByteLength = (Capabilities->OutputReportByteLength > 0 ? Capabilities->OutputReportByteLength + 1 : 0); + Capabilities->FeatureReportByteLength = (Capabilities->FeatureReportByteLength > 0 ? Capabilities->FeatureReportByteLength + 1 : 0); + + // + // get number of link collection nodes + // + Capabilities->NumberLinkCollectionNodes = HidParser_GetTotalCollectionCount(Parser); + + // + // get data indices + // + Capabilities->NumberInputDataIndices = HidParser_GetReportItemTypeCountFromReportType(Parser, HID_REPORT_TYPE_INPUT, TRUE); + Capabilities->NumberOutputDataIndices = HidParser_GetReportItemTypeCountFromReportType(Parser, HID_REPORT_TYPE_OUTPUT, TRUE); + Capabilities->NumberFeatureDataIndices = HidParser_GetReportItemTypeCountFromReportType(Parser, HID_REPORT_TYPE_FEATURE, TRUE); + + // + // get value caps + // + Capabilities->NumberInputValueCaps = HidParser_GetReportItemTypeCountFromReportType(Parser, HID_REPORT_TYPE_INPUT, FALSE); + Capabilities->NumberOutputValueCaps = HidParser_GetReportItemTypeCountFromReportType(Parser, HID_REPORT_TYPE_OUTPUT, FALSE); + Capabilities->NumberFeatureValueCaps = HidParser_GetReportItemTypeCountFromReportType(Parser, HID_REPORT_TYPE_FEATURE, FALSE); + + + // + // get button caps + // + Capabilities->NumberInputButtonCaps = HidParser_GetReportItemCountFromReportType(Parser, HID_REPORT_TYPE_INPUT); + Capabilities->NumberOutputButtonCaps = HidParser_GetReportItemCountFromReportType(Parser, HID_REPORT_TYPE_OUTPUT); + Capabilities->NumberFeatureButtonCaps = HidParser_GetReportItemCountFromReportType(Parser, HID_REPORT_TYPE_FEATURE); + + // + // done + // + return HIDP_STATUS_SUCCESS; +} + +HIDAPI +ULONG +NTAPI +HidParser_MaxUsageListLength( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage OPTIONAL) +{ + // + // FIXME test what should be returned when usage page is not defined + // + if (UsagePage == HID_USAGE_PAGE_UNDEFINED) + { + // + // implement me + // + UNIMPLEMENTED + + // + // invalid report + // + return 0; + } + + if (ReportType == HidP_Input) + { + // + // input report + // + return HidParser_GetMaxUsageListLengthWithReportAndPage(Parser, HID_REPORT_TYPE_INPUT, UsagePage); + } + else if (ReportType == HidP_Output) + { + // + // input report + // + return HidParser_GetMaxUsageListLengthWithReportAndPage(Parser, HID_REPORT_TYPE_OUTPUT, UsagePage); + } + else if (ReportType == HidP_Feature) + { + // + // input report + // + return HidParser_GetMaxUsageListLengthWithReportAndPage(Parser, HID_REPORT_TYPE_FEATURE, UsagePage); + } + else + { + // + // invalid report type + // + return 0; + } +} + +#undef HidParser_GetButtonCaps + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetButtonCaps( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN PHIDP_BUTTON_CAPS ButtonCaps, + IN PUSHORT ButtonCapsLength) +{ + return HidParser_GetSpecificButtonCaps(Parser, ReportType, HID_USAGE_PAGE_UNDEFINED, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_PAGE_UNDEFINED, ButtonCaps, (PULONG)ButtonCapsLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetSpecificValueCaps( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PHIDP_VALUE_CAPS ValueCaps, + IN OUT PULONG ValueCapsLength) +{ + HIDPARSER_STATUS ParserStatus; + + // + // FIXME: implement searching in specific collection + // + ASSERT(LinkCollection == HIDP_LINK_COLLECTION_UNSPECIFIED); + + if (ReportType == HidP_Input) + { + // + // input report + // + ParserStatus = HidParser_GetSpecificValueCapsWithReport(Parser, HID_REPORT_TYPE_INPUT, UsagePage, Usage, ValueCaps, ValueCapsLength); + } + else if (ReportType == HidP_Output) + { + // + // input report + // + ParserStatus = HidParser_GetSpecificValueCapsWithReport(Parser, HID_REPORT_TYPE_OUTPUT, UsagePage, Usage, ValueCaps, ValueCapsLength); + } + else if (ReportType == HidP_Feature) + { + // + // input report + // + ParserStatus = HidParser_GetSpecificValueCapsWithReport(Parser, HID_REPORT_TYPE_FEATURE, UsagePage, Usage, ValueCaps, ValueCapsLength); + } + else + { + // + // invalid report type + // + return HIDP_STATUS_INVALID_REPORT_TYPE; + } + + + if (ParserStatus == HIDPARSER_STATUS_SUCCESS) + { + // + // success + // + return HIDP_STATUS_SUCCESS; + } + + // + // translate error + // + return TranslateHidParserStatus(ParserStatus); +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_UsageListDifference( + IN PUSAGE PreviousUsageList, + IN PUSAGE CurrentUsageList, + OUT PUSAGE BreakUsageList, + OUT PUSAGE MakeUsageList, + IN ULONG UsageListLength) +{ + ULONG Index, SubIndex, bFound, BreakUsageIndex = 0, MakeUsageIndex = 0; + USAGE CurrentUsage, Usage; + + if (UsageListLength) + { + Index = 0; + do + { + /* get current usage */ + CurrentUsage = PreviousUsageList[Index]; + + /* is the end of list reached? */ + if (!CurrentUsage) + break; + + /* start searching in current usage list */ + SubIndex = 0; + bFound = FALSE; + do + { + /* get usage of current list */ + Usage = CurrentUsageList[SubIndex]; + + /* end of list reached? */ + if (!Usage) + break; + + /* check if it matches the current one */ + if (CurrentUsage == Usage) + { + /* it does */ + bFound = TRUE; + break; + } + + /* move to next usage */ + SubIndex++; + }while(SubIndex < UsageListLength); + + /* was the usage found ?*/ + if (!bFound) + { + /* store it in the break usage list */ + BreakUsageList[BreakUsageIndex] = CurrentUsage; + BreakUsageIndex++; + } + + /* move to next usage */ + Index++; + + }while(Index < UsageListLength); + + /* now process the new items */ + Index = 0; + do + { + /* get current usage */ + CurrentUsage = CurrentUsageList[Index]; + + /* is the end of list reached? */ + if (!CurrentUsage) + break; + + /* start searching in current usage list */ + SubIndex = 0; + bFound = FALSE; + do + { + /* get usage of previous list */ + Usage = PreviousUsageList[SubIndex]; + + /* end of list reached? */ + if (!Usage) + break; + + /* check if it matches the current one */ + if (CurrentUsage == Usage) + { + /* it does */ + bFound = TRUE; + break; + } + + /* move to next usage */ + SubIndex++; + }while(SubIndex < UsageListLength); + + /* was the usage found ?*/ + if (!bFound) + { + /* store it in the make usage list */ + MakeUsageList[MakeUsageIndex] = CurrentUsage; + MakeUsageIndex++; + } + + /* move to next usage */ + Index++; + + }while(Index < UsageListLength); + } + + /* does the break list contain empty entries */ + if (BreakUsageIndex < UsageListLength) + { + /* zeroize entries */ + RtlZeroMemory(&BreakUsageList[BreakUsageIndex], sizeof(USAGE) * (UsageListLength - BreakUsageIndex)); + } + + /* does the make usage list contain empty entries */ + if (MakeUsageIndex < UsageListLength) + { + /* zeroize entries */ + RtlZeroMemory(&MakeUsageList[MakeUsageIndex], sizeof(USAGE) * (UsageListLength - MakeUsageIndex)); + } + + /* done */ + return HIDP_STATUS_SUCCESS; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsages( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + OUT USAGE *UsageList, + IN OUT PULONG UsageLength, + IN PCHAR Report, + IN ULONG ReportLength) +{ + HIDPARSER_STATUS ParserStatus; + + // + // FIXME: implement searching in specific collection + // + ASSERT(LinkCollection == HIDP_LINK_COLLECTION_UNSPECIFIED); + + if (ReportType == HidP_Input) + { + // + // input report + // + ParserStatus = HidParser_GetUsagesWithReport(Parser, HID_REPORT_TYPE_INPUT, UsagePage, UsageList, UsageLength, Report, ReportLength); + } + else if (ReportType == HidP_Output) + { + // + // input report + // + ParserStatus = HidParser_GetUsagesWithReport(Parser, HID_REPORT_TYPE_OUTPUT, UsagePage, UsageList, UsageLength, Report, ReportLength); + } + else if (ReportType == HidP_Feature) + { + // + // input report + // + ParserStatus = HidParser_GetUsagesWithReport(Parser, HID_REPORT_TYPE_FEATURE, UsagePage, UsageList, UsageLength, Report, ReportLength); + } + else + { + // + // invalid report type + // + return HIDP_STATUS_INVALID_REPORT_TYPE; + } + + if (ParserStatus == HIDPARSER_STATUS_SUCCESS) + { + // + // success + // + return HIDP_STATUS_SUCCESS; + } + + // + // translate error + // + return TranslateHidParserStatus(ParserStatus); +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetScaledUsageValue( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + OUT PLONG UsageValue, + IN PCHAR Report, + IN ULONG ReportLength) +{ + HIDPARSER_STATUS ParserStatus; + + // + // FIXME: implement searching in specific collection + // + ASSERT(LinkCollection == HIDP_LINK_COLLECTION_UNSPECIFIED); + + if (ReportType == HidP_Input) + { + // + // input report + // + ParserStatus = HidParser_GetScaledUsageValueWithReport(Parser, HID_REPORT_TYPE_INPUT, UsagePage, Usage, UsageValue, Report, ReportLength); + } + else if (ReportType == HidP_Output) + { + // + // input report + // + ParserStatus = HidParser_GetScaledUsageValueWithReport(Parser, HID_REPORT_TYPE_OUTPUT, UsagePage, Usage, UsageValue, Report, ReportLength); + } + else if (ReportType == HidP_Feature) + { + // + // input report + // + ParserStatus = HidParser_GetScaledUsageValueWithReport(Parser, HID_REPORT_TYPE_FEATURE, UsagePage, Usage, UsageValue, Report, ReportLength); + } + else + { + // + // invalid report type + // + return HIDP_STATUS_INVALID_REPORT_TYPE; + } + + if (ParserStatus == HIDPARSER_STATUS_SUCCESS) + { + // + // success + // + return HIDP_STATUS_SUCCESS; + } + + // + // translate error + // + return TranslateHidParserStatus(ParserStatus); +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsagesEx( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USHORT LinkCollection, + OUT PUSAGE_AND_PAGE ButtonList, + IN OUT ULONG *UsageLength, + IN PCHAR Report, + IN ULONG ReportLength) +{ + return HidParser_GetUsages(Parser, ReportType, HID_USAGE_PAGE_UNDEFINED, LinkCollection, (PUSAGE)ButtonList, UsageLength, Report, ReportLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_UsageAndPageListDifference( + IN PUSAGE_AND_PAGE PreviousUsageList, + IN PUSAGE_AND_PAGE CurrentUsageList, + OUT PUSAGE_AND_PAGE BreakUsageList, + OUT PUSAGE_AND_PAGE MakeUsageList, + IN ULONG UsageListLength) +{ + ULONG Index, SubIndex, BreakUsageListIndex = 0, MakeUsageListIndex = 0, bFound; + PUSAGE_AND_PAGE CurrentUsage, Usage; + + if (UsageListLength) + { + /* process removed usages */ + Index = 0; + do + { + /* get usage from current index */ + CurrentUsage = &PreviousUsageList[Index]; + + /* end of list reached? */ + if (CurrentUsage->Usage == 0 && CurrentUsage->UsagePage == 0) + break; + + /* search in current list */ + SubIndex = 0; + bFound = FALSE; + do + { + /* get usage */ + Usage = &CurrentUsageList[SubIndex]; + + /* end of list reached? */ + if (Usage->Usage == 0 && Usage->UsagePage == 0) + break; + + /* does it match */ + if (Usage->Usage == CurrentUsage->Usage && Usage->UsagePage == CurrentUsage->UsagePage) + { + /* found match */ + bFound = TRUE; + } + + /* move to next index */ + SubIndex++; + + }while(SubIndex < UsageListLength); + + if (!bFound) + { + /* store it in break usage list */ + BreakUsageList[BreakUsageListIndex].Usage = CurrentUsage->Usage; + BreakUsageList[BreakUsageListIndex].UsagePage = CurrentUsage->UsagePage; + BreakUsageListIndex++; + } + + /* move to next index */ + Index++; + + }while(Index < UsageListLength); + + /* process new usages */ + Index = 0; + do + { + /* get usage from current index */ + CurrentUsage = &CurrentUsageList[Index]; + + /* end of list reached? */ + if (CurrentUsage->Usage == 0 && CurrentUsage->UsagePage == 0) + break; + + /* search in current list */ + SubIndex = 0; + bFound = FALSE; + do + { + /* get usage */ + Usage = &PreviousUsageList[SubIndex]; + + /* end of list reached? */ + if (Usage->Usage == 0 && Usage->UsagePage == 0) + break; + + /* does it match */ + if (Usage->Usage == CurrentUsage->Usage && Usage->UsagePage == CurrentUsage->UsagePage) + { + /* found match */ + bFound = TRUE; + } + + /* move to next index */ + SubIndex++; + + }while(SubIndex < UsageListLength); + + if (!bFound) + { + /* store it in break usage list */ + MakeUsageList[MakeUsageListIndex].Usage = CurrentUsage->Usage; + MakeUsageList[MakeUsageListIndex].UsagePage = CurrentUsage->UsagePage; + MakeUsageListIndex++; + } + + /* move to next index */ + Index++; + }while(Index < UsageListLength); + } + + /* are there remaining free list entries */ + if (BreakUsageListIndex < UsageListLength) + { + /* zero them */ + RtlZeroMemory(&BreakUsageList[BreakUsageListIndex], (UsageListLength - BreakUsageListIndex) * sizeof(USAGE_AND_PAGE)); + } + + /* are there remaining free list entries */ + if (MakeUsageListIndex < UsageListLength) + { + /* zero them */ + RtlZeroMemory(&MakeUsageList[MakeUsageListIndex], (UsageListLength - MakeUsageListIndex) * sizeof(USAGE_AND_PAGE)); + } + + /* done */ + return HIDP_STATUS_SUCCESS; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetSpecificButtonCaps( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PHIDP_BUTTON_CAPS ButtonCaps, + IN OUT PULONG ButtonCapsLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetData( + IN HIDP_REPORT_TYPE ReportType, + OUT PHIDP_DATA DataList, + IN OUT PULONG DataLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetExtendedAttributes( + IN HIDP_REPORT_TYPE ReportType, + IN USHORT DataIndex, + IN PHIDP_PREPARSED_DATA PreparsedData, + OUT PHIDP_EXTENDED_ATTRIBUTES Attributes, + IN OUT PULONG LengthAttributes) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetLinkCollectionNodes( + OUT PHIDP_LINK_COLLECTION_NODE LinkCollectionNodes, + IN OUT PULONG LinkCollectionNodesLength, + IN PHIDP_PREPARSED_DATA PreparsedData) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsageValue( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PULONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS +NTAPI +HidParser_SysPowerEvent ( + IN PCHAR HidPacket, + IN USHORT HidPacketLength, + IN PHIDP_PREPARSED_DATA Ppd, + OUT PULONG OutputBuffer) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +HidParser_SysPowerCaps ( + IN PHIDP_PREPARSED_DATA Ppd, + OUT PULONG OutputBuffer) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsageValueArray( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + OUT PCHAR UsageValue, + IN USHORT UsageValueByteLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_UnsetUsages( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN PUSAGE UsageList, + IN OUT PULONG UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_TranslateUsagesToI8042ScanCodes( + IN PUSAGE ChangedUsageList, + IN ULONG UsageListLength, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_TranslateUsageAndPagesToI8042ScanCodes( + IN PUSAGE_AND_PAGE ChangedUsageList, + IN ULONG UsageListLength, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetUsages( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN PUSAGE UsageList, + IN OUT PULONG UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetUsageValueArray( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + IN PCHAR UsageValue, + IN USHORT UsageValueByteLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetUsageValue( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + IN ULONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetScaledUsageValue( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + IN LONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetData( + IN HIDP_REPORT_TYPE ReportType, + IN PHIDP_DATA DataList, + IN OUT PULONG DataLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +ULONG +NTAPI +HidParser_MaxDataListLength( + IN HIDP_REPORT_TYPE ReportType, + IN PHIDP_PREPARSED_DATA PreparsedData) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_InitializeReportForID( + IN HIDP_REPORT_TYPE ReportType, + IN UCHAR ReportID, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +#undef HidParser_GetValueCaps + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetValueCaps( + HIDP_REPORT_TYPE ReportType, + PHIDP_VALUE_CAPS ValueCaps, + PULONG ValueCapsLength, + PHIDP_PREPARSED_DATA PreparsedData) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} diff --git a/lib/drivers/hidparser/hidparser.h b/lib/drivers/hidparser/hidparser.h new file mode 100644 index 00000000000..f305c221963 --- /dev/null +++ b/lib/drivers/hidparser/hidparser.h @@ -0,0 +1,427 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/hidparser/hidparser.c + * PURPOSE: HID Parser + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#pragma once + +#define _HIDPI_ +#define _HIDPI_NO_FUNCTION_MACROS_ +#include +#include +#include +#include + +// +// function prototypes +// +typedef PVOID (NTAPI *PHIDPARSER_ALLOC_FUNCTION)(ULONG Size); +typedef VOID (NTAPI *PHIDPARSER_FREE_FUNCTION)(PVOID Item); +typedef VOID (NTAPI *PHIDPARSER_ZERO_FUNCTION)(PVOID Item, ULONG Size); +typedef VOID (NTAPI *PHIDPARSER_COPY_FUNCTION)(PVOID Target, PVOID Source, ULONG Size); +typedef VOID (NTAPI *PHIDPARSER_DEBUG_FUNCTION)(LPCSTR Src, ...); + +// +// status code +// +typedef long HIDPARSER_STATUS; + +// +// result codes +// +typedef enum +{ + HIDPARSER_STATUS_SUCCESS = 0, + HIDPARSER_STATUS_INSUFFICIENT_RESOURCES = -1, + HIDPARSER_STATUS_NOT_IMPLEMENTED = -2, + HIDPARSER_STATUS_REPORT_NOT_FOUND = -3, + HIDPARSER_STATUS_COLLECTION_NOT_FOUND = -4, + HIDPARSER_STATUS_INVALID_REPORT_LENGTH = -5, + HIDPARSER_STATUS_INVALID_REPORT_TYPE = -6, + HIDPARSER_STATUS_BUFFER_TOO_SMALL = -7, + HIDPARSER_STATUS_USAGE_NOT_FOUND = 8 +}HIDPARSER_STATUS_CODES; + +typedef struct +{ + // + // size of struct + // + unsigned long Size; + + // + // allocation function + // + PHIDPARSER_ALLOC_FUNCTION Alloc; + + // + // free function + // + PFREE_FUNCTION Free; + + // + // zero function + // + PHIDPARSER_ZERO_FUNCTION Zero; + + // + // copy function + // + PHIDPARSER_COPY_FUNCTION Copy; + + // + // debug function + // + PHIDPARSER_DEBUG_FUNCTION Debug; + + // + // parser context + // + void * ParserContext; + +}HID_PARSER, *PHID_PARSER; + +HIDPARSER_STATUS +HidParser_AllocateParser( + IN PHIDPARSER_ALLOC_FUNCTION AllocFunction, + IN PHIDPARSER_FREE_FUNCTION FreeFunction, + IN PHIDPARSER_ZERO_FUNCTION ZeroFunction, + IN PHIDPARSER_COPY_FUNCTION CopyFunction, + IN PHIDPARSER_DEBUG_FUNCTION DebugFunction, + OUT PHID_PARSER *OutParser); + +VOID +HidParser_InitParser( + IN PHIDPARSER_ALLOC_FUNCTION AllocFunction, + IN PHIDPARSER_FREE_FUNCTION FreeFunction, + IN PHIDPARSER_ZERO_FUNCTION ZeroFunction, + IN PHIDPARSER_COPY_FUNCTION CopyFunction, + IN PHIDPARSER_DEBUG_FUNCTION DebugFunction, + IN PVOID ParserContext, + OUT PHID_PARSER Parser); + +NTSTATUS +NTAPI +HidParser_GetCollectionDescription( + IN PHID_PARSER Parser, + IN PHIDP_REPORT_DESCRIPTOR ReportDesc, + IN ULONG DescLength, + IN POOL_TYPE PoolType, + OUT PHIDP_DEVICE_DESC DeviceDescription); + +VOID +NTAPI +HidParser_FreeCollectionDescription( + IN PHID_PARSER Parser, + IN PHIDP_DEVICE_DESC DeviceDescription); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetCaps( + IN PHID_PARSER Parser, + OUT PHIDP_CAPS Capabilities); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetSpecificValueCaps( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PHIDP_VALUE_CAPS ValueCaps, + IN OUT PULONG ValueCapsLength); + + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetButtonCaps( + IN PHID_PARSER Parser, + HIDP_REPORT_TYPE ReportType, + PHIDP_BUTTON_CAPS ButtonCaps, + PUSHORT ButtonCapsLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetSpecificButtonCaps( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PHIDP_BUTTON_CAPS ButtonCaps, + IN OUT PULONG ButtonCapsLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetScaledUsageValue( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + OUT PLONG UsageValue, + IN PCHAR Report, + IN ULONG ReportLength); + + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetData( + IN HIDP_REPORT_TYPE ReportType, + OUT PHIDP_DATA DataList, + IN OUT PULONG DataLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetExtendedAttributes( + IN HIDP_REPORT_TYPE ReportType, + IN USHORT DataIndex, + IN PHIDP_PREPARSED_DATA PreparsedData, + OUT PHIDP_EXTENDED_ATTRIBUTES Attributes, + IN OUT PULONG LengthAttributes); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetLinkCollectionNodes( + OUT PHIDP_LINK_COLLECTION_NODE LinkCollectionNodes, + IN OUT PULONG LinkCollectionNodesLength, + IN PHIDP_PREPARSED_DATA PreparsedData); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsageValue( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PULONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_UsageListDifference( + IN PUSAGE PreviousUsageList, + IN PUSAGE CurrentUsageList, + OUT PUSAGE BreakUsageList, + OUT PUSAGE MakeUsageList, + IN ULONG UsageListLength); + + +HIDAPI +ULONG +NTAPI +HidParser_MaxUsageListLength( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage OPTIONAL); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsages( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + OUT USAGE *UsageList, + IN OUT ULONG *UsageLength, + IN PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsagesEx( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USHORT LinkCollection, + OUT PUSAGE_AND_PAGE ButtonList, + IN OUT ULONG *UsageLength, + IN PCHAR Report, + IN ULONG ReportLength); + + +NTSTATUS +NTAPI +HidParser_SysPowerEvent ( + IN PCHAR HidPacket, + IN USHORT HidPacketLength, + IN PHIDP_PREPARSED_DATA Ppd, + OUT PULONG OutputBuffer); + +NTSTATUS +NTAPI +HidParser_SysPowerCaps ( + IN PHIDP_PREPARSED_DATA Ppd, + OUT PULONG OutputBuffer); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsageValueArray( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + OUT PCHAR UsageValue, + IN USHORT UsageValueByteLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength); + + +HIDAPI +NTSTATUS +NTAPI +HidParser_UsageAndPageListDifference( + IN PUSAGE_AND_PAGE PreviousUsageList, + IN PUSAGE_AND_PAGE CurrentUsageList, + OUT PUSAGE_AND_PAGE BreakUsageList, + OUT PUSAGE_AND_PAGE MakeUsageList, + IN ULONG UsageListLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_UnsetUsages( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN PUSAGE UsageList, + IN OUT PULONG UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_TranslateUsagesToI8042ScanCodes( + IN PUSAGE ChangedUsageList, + IN ULONG UsageListLength, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext); + +HIDAPI +NTSTATUS +NTAPI +HidParser_TranslateUsageAndPagesToI8042ScanCodes( + IN PUSAGE_AND_PAGE ChangedUsageList, + IN ULONG UsageListLength, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext); + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetUsages( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN PUSAGE UsageList, + IN OUT PULONG UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetUsageValueArray( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + IN PCHAR UsageValue, + IN USHORT UsageValueByteLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetUsageValue( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + IN ULONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetScaledUsageValue( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + IN LONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetData( + IN HIDP_REPORT_TYPE ReportType, + IN PHIDP_DATA DataList, + IN OUT PULONG DataLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +ULONG +NTAPI +HidParser_MaxDataListLength( + IN HIDP_REPORT_TYPE ReportType, + IN PHIDP_PREPARSED_DATA PreparsedData); + +HIDAPI +NTSTATUS +NTAPI +HidParser_InitializeReportForID( + IN HIDP_REPORT_TYPE ReportType, + IN UCHAR ReportID, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetValueCaps( + HIDP_REPORT_TYPE ReportType, + PHIDP_VALUE_CAPS ValueCaps, + PULONG ValueCapsLength, + PHIDP_PREPARSED_DATA PreparsedData); diff --git a/lib/drivers/hidparser/parser.c b/lib/drivers/hidparser/parser.c new file mode 100644 index 00000000000..d86b6df9bd0 --- /dev/null +++ b/lib/drivers/hidparser/parser.c @@ -0,0 +1,1347 @@ +/* + * PROJECT: ReactOS HID Parser Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/hidparser/parser.c + * PURPOSE: HID Parser + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + + +#include "parser.h" + +static UCHAR ItemSize[4] = { 0, 1, 2, 4 }; + +VOID +HidParser_DeleteReport( + IN PHID_PARSER Parser, + IN PHID_REPORT Report) +{ + // + // not implemented + // +} + +VOID +HidParser_FreeCollection( + IN PHID_PARSER Parser, + IN PHID_COLLECTION Collection) +{ + // + // not implemented + // +} + +HIDPARSER_STATUS +HidParser_AllocateCollection( + IN PHID_PARSER Parser, + IN PHID_COLLECTION ParentCollection, + IN UCHAR Type, + IN PLOCAL_ITEM_STATE LocalItemState, + OUT PHID_COLLECTION * OutCollection) +{ + PHID_COLLECTION Collection; + USAGE_VALUE UsageValue; + + // + // first allocate the collection + // + Collection = (PHID_COLLECTION)Parser->Alloc(sizeof(HID_COLLECTION)); + if (!Collection) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // init collection + // + Collection->Root = ParentCollection; + Collection->Type = Type; + Collection->StringID = LocalItemState->StringIndex; + Collection->PhysicalID = LocalItemState->DesignatorIndex; + + // + // set Usage + // + ASSERT(LocalItemState); + ASSERT(LocalItemState->UsageStack); + + if (LocalItemState->UsageStackUsed > 0) + { + // + // usage value from first local stack item + // + UsageValue.u.Extended = LocalItemState->UsageStack[0].u.Extended; + } + else if (LocalItemState->UsageMinimumSet) + { + // + // use value from minimum + // + UsageValue.u.Extended = LocalItemState->UsageMinimum.u.Extended; + } + else if (LocalItemState->UsageMaximumSet) + { + // + // use value from maximum + // + UsageValue.u.Extended = LocalItemState->UsageMaximum.u.Extended; + } + else if (Type == COLLECTION_LOGICAL) + { + // + // root collection + // + UsageValue.u.Extended = 0; + } + else + { + // + // no usage set + // + Parser->Debug("HIDPARSE] No usage set\n"); + UsageValue.u.Extended = 0; + } + + // + // store usage + // + Collection->Usage = UsageValue.u.Extended; + + // + // store result + // + *OutCollection = Collection; + + // + // done + // + return HIDPARSER_STATUS_SUCCESS; +} + + +VOID +HidParser_ResetParser( + OUT PHID_PARSER Parser) +{ + ULONG Index; + PHID_PARSER_CONTEXT ParserContext; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + + + // + // delete all reports + // + for(Index = 0; Index < ParserContext->ReportCount; Index++) + { + // + // delete report + // + HidParser_DeleteReport(Parser, ParserContext->Reports[Index]); + } + + if (ParserContext->ReportCount && ParserContext->Reports) + { + // + // free report array + // + Parser->Free(ParserContext->Reports); + } + + if (ParserContext->RootCollection) + { + // + // delete root collection + // + HidParser_FreeCollection(Parser, ParserContext->RootCollection); + } + + // + // reinit parser + // + ParserContext->ReportCount = 0; + ParserContext->Reports = NULL; + ParserContext->RootCollection = NULL; + ParserContext->UseReportIDs = FALSE; + + // + // zero item states + // + Parser->Zero(&ParserContext->GlobalItemState, sizeof(GLOBAL_ITEM_STATE)); + Parser->Zero(&ParserContext->LocalItemState, sizeof(LOCAL_ITEM_STATE)); +} + +HIDPARSER_STATUS +HidParser_AddCollection( + IN PHID_PARSER Parser, + IN PHID_COLLECTION CurrentCollection, + IN PHID_COLLECTION NewCollection) +{ + PHID_COLLECTION * NewAllocCollection; + ULONG CollectionCount; + + // + // increment collection array + // + CollectionCount = CurrentCollection->NodeCount + 1; + + // + // allocate new collection + // + NewAllocCollection = (PHID_COLLECTION*)Parser->Alloc(sizeof(PHID_COLLECTION) * CollectionCount); + if (!NewAllocCollection) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + if (CurrentCollection->NodeCount) + { + // + // copy old array + // + Parser->Copy(NewAllocCollection, CurrentCollection->Nodes, CurrentCollection->NodeCount * sizeof(PHID_COLLECTION)); + + // + // delete old array + // + Parser->Free(CurrentCollection->Nodes); + } + + // + // insert new item + // + NewAllocCollection[CurrentCollection->NodeCount] = (struct __HID_COLLECTION__*)NewCollection; + + + // + // store new array + // + CurrentCollection->Nodes = NewAllocCollection; + CurrentCollection->NodeCount++; + + // + // done + // + return HIDPARSER_STATUS_SUCCESS; +} + +HIDPARSER_STATUS +HidParser_FindReport( + IN PHID_PARSER Parser, + IN UCHAR ReportType, + IN UCHAR ReportID, + OUT PHID_REPORT *OutReport) +{ + PHID_PARSER_CONTEXT ParserContext; + ULONG Index; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + ASSERT(ParserContext); + + for(Index = 0; Index < ParserContext->ReportCount; Index++) + { + if (ParserContext->Reports[Index]->Type == ReportType && ParserContext->Reports[Index]->ReportID == ReportID) + { + // + // found report + // + *OutReport = ParserContext->Reports[Index]; + return HIDPARSER_STATUS_SUCCESS; + } + } + + // + // no such report found + // + *OutReport = NULL; + return HIDPARSER_STATUS_REPORT_NOT_FOUND; +} + +HIDPARSER_STATUS +HidParser_AllocateReport( + IN PHID_PARSER Parser, + IN UCHAR ReportType, + IN UCHAR ReportID, + OUT PHID_REPORT *OutReport) +{ + PHID_REPORT Report; + + // + // allocate report + // + Report = (PHID_REPORT)Parser->Alloc(sizeof(HID_REPORT)); + if (!Report) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // init report + // + Report->ReportID = ReportID; + Report->Type = ReportType; + + // + // done + // + *OutReport = Report; + return HIDPARSER_STATUS_SUCCESS; +} + +HIDPARSER_STATUS +HidParser_AddReport( + IN PHID_PARSER Parser, + IN PHID_REPORT NewReport) +{ + PHID_REPORT * NewReportArray; + PHID_PARSER_CONTEXT ParserContext; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)Parser->ParserContext; + ASSERT(ParserContext); + + // + // allocate new report array + // + NewReportArray = (PHID_REPORT*)Parser->Alloc(sizeof(PHID_REPORT) * (ParserContext->ReportCount + 1)); + if (!NewReportArray) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + if (ParserContext->ReportCount) + { + // + // copy old array contents + // + Parser->Copy(NewReportArray, ParserContext->Reports, sizeof(PHID_REPORT) * ParserContext->ReportCount); + + // + // free old array + // + Parser->Free(ParserContext->Reports); + } + + // + // store result + // + NewReportArray[ParserContext->ReportCount] = NewReport; + ParserContext->Reports = NewReportArray; + ParserContext->ReportCount++; + + // + // completed successfully + // + return HIDPARSER_STATUS_SUCCESS; +} + +HIDPARSER_STATUS +HidParser_GetReport( + IN PHID_PARSER Parser, + IN UCHAR ReportType, + IN UCHAR ReportID, + IN UCHAR CreateIfNotExists, + OUT PHID_REPORT *OutReport) +{ + HIDPARSER_STATUS Status; + + // + // try finding existing report + // + Status = HidParser_FindReport(Parser, ReportType, ReportID, OutReport); + if (Status == HIDPARSER_STATUS_SUCCESS || CreateIfNotExists == FALSE) + { + // + // founed report + // + return Status; + } + + // + // allocate new report + // + Status = HidParser_AllocateReport(Parser, ReportType, ReportID, OutReport); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // failed to allocate report + // + return Status; + } + + // + // add report + // + Status = HidParser_AddReport(Parser, *OutReport); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // failed to allocate report + // + Parser->Free(*OutReport); + } + + // + // done + // + return Status; +} + + +HIDPARSER_STATUS +HidParser_ReserveCollectionItems( + IN PHID_PARSER Parser, + IN PHID_COLLECTION Collection, + IN ULONG ReportCount) +{ + PHID_REPORT_ITEM * NewReportArray; + + if (Collection->ItemCount + ReportCount <= Collection->ItemCountAllocated) + { + // + // enough space for the next items + // + return HIDPARSER_STATUS_SUCCESS; + } + + // + // allocate report array + // + NewReportArray = (PHID_REPORT_ITEM*)Parser->Alloc(sizeof(PHID_REPORT) * (Collection->ItemCountAllocated + ReportCount)); + if (!NewReportArray) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // are there any items + // + if (Collection->ItemCount) + { + // + // copy old items + // + Parser->Copy(NewReportArray, Collection->Items, sizeof(PHID_REPORT_ITEM) * Collection->ItemCount); + + // + // free old item + // + Parser->Free(Collection->Items); + } + + // + // replace array + // + Collection->Items = NewReportArray; + Collection->ItemCountAllocated += ReportCount; + + // + // completed sucessfully + // + return HIDPARSER_STATUS_SUCCESS; +} + + +HIDPARSER_STATUS +HidParser_ReserveReportItems( + IN PHID_PARSER Parser, + IN PHID_REPORT Report, + IN ULONG ReportCount) +{ + PHID_REPORT_ITEM * NewReportArray; + + if (Report->ItemCount + ReportCount <= Report->ItemAllocated) + { + // + // enough space for the next items + // + return HIDPARSER_STATUS_SUCCESS; + } + + // + // allocate report array + // + NewReportArray = (PHID_REPORT_ITEM*)Parser->Alloc(sizeof(PHID_REPORT_ITEM) * (Report->ItemAllocated + ReportCount)); + if (!NewReportArray) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // are there any items + // + if (Report->ItemCount) + { + // + // copy old items + // + Parser->Copy(NewReportArray, Report->Items, sizeof(PHID_REPORT_ITEM) * Report->ItemCount); + + // + // free old item + // + Parser->Free(Report->Items); + } + + // + // replace array + // + Report->Items = NewReportArray; + Report->ItemAllocated += ReportCount; + + // + // completed sucessfully + // + return HIDPARSER_STATUS_SUCCESS; +} + +HIDPARSER_STATUS +HidParser_AllocateReportItem( + IN PHID_PARSER Parser, + OUT PHID_REPORT_ITEM * OutReportItem) +{ + PHID_REPORT_ITEM ReportItem; + + // + // allocate report item + // + ReportItem = (PHID_REPORT_ITEM)Parser->Alloc(sizeof(HID_REPORT_ITEM)); + if (!ReportItem) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // store result + // + *OutReportItem = ReportItem; + return HIDPARSER_STATUS_SUCCESS; +} + +VOID +HidParser_AddReportItemToReport( + IN OUT PHID_REPORT Report, + IN PHID_REPORT_ITEM ReportItem) +{ + // + // there should be space in item array + // + ASSERT(Report->ItemCount + 1 <= Report->ItemAllocated); + + // + // store item + // + Report->Items[Report->ItemCount] = ReportItem; + Report->ItemCount++; +} + +VOID +HidParser_AddReportItemToCollection( + IN OUT PHID_COLLECTION Collection, + IN PHID_REPORT_ITEM ReportItem) +{ + // + // there should be space in item array + // + ASSERT(Collection->ItemCount + 1 <= Collection->ItemCountAllocated); + + // + // store item + // + Collection->Items[Collection->ItemCount] = ReportItem; + Collection->ItemCount++; +} + +VOID +HidParser_SignRange( + IN ULONG Minimum, + IN ULONG Maximum, + OUT PULONG NewMinimum, + OUT PULONG NewMaximum) +{ + ULONG Mask = 0x80000000; + ULONG Index; + + for (Index = 0; Index < 4; Index++) + { + if (Minimum & Mask) + { + Minimum |= Mask; + if (Maximum & Mask) + Maximum |= Mask; + return; + } + + Mask >>= 8; + Mask |= 0xff000000; + } + + *NewMinimum = Minimum; + *NewMaximum = Maximum; +} + +HIDPARSER_STATUS +HidParser_InitReportItem( + IN PHID_REPORT Report, + IN PHID_REPORT_ITEM ReportItem, + IN PGLOBAL_ITEM_STATE GlobalItemState, + IN PLOCAL_ITEM_STATE LocalItemState, + IN PMAIN_ITEM_DATA ItemData, + IN ULONG ReportItemIndex) +{ + ULONG LogicalMinimum; + ULONG LogicalMaximum; + ULONG PhysicalMinimum; + ULONG PhysicalMaximum; + ULONG UsageMinimum; + ULONG UsageMaximum; + USAGE_VALUE UsageValue; + + // + // get logical bounds + // + LogicalMinimum = GlobalItemState->LogicalMinimum; + LogicalMaximum = GlobalItemState->LogicialMaximum; + if (LogicalMinimum > LogicalMaximum) + { + // + // make them signed + // + HidParser_SignRange(LogicalMinimum, LogicalMaximum, &LogicalMinimum, &LogicalMaximum); + } + //ASSERT(LogicalMinimum <= LogicalMaximum); + + // + // get physical bounds + // + PhysicalMinimum = GlobalItemState->PhysicalMinimum; + PhysicalMaximum = GlobalItemState->PhysicalMaximum; + if (PhysicalMinimum > PhysicalMaximum) + { + // + // make them signed + // + HidParser_SignRange(PhysicalMinimum, PhysicalMaximum, &PhysicalMinimum, &PhysicalMaximum); + } + //ASSERT(PhysicalMinimum <= PhysicalMaximum); + + // + // get usage bounds + // + UsageMinimum = 0; + UsageMaximum = 0; + if (ItemData->ArrayVariable == FALSE) + { + // + // get usage bounds + // + UsageMinimum = LocalItemState->UsageMinimum.u.Extended; + UsageMaximum = LocalItemState->UsageMaximum.u.Extended; + } + else + { + // + // get usage value from stack + // + if (ReportItemIndex < LocalItemState->UsageStackUsed) + { + // + // use stack item + // + UsageValue = LocalItemState->UsageStack[ReportItemIndex]; + } + else + { + // + // get usage minimum from local state + // + UsageValue = LocalItemState->UsageMinimum; + ASSERT(LocalItemState->UsageMinimumSet); + ASSERT(LocalItemState->UsageMaximumSet); + + // + // append item index + // + UsageValue.u.Extended += ReportItemIndex; + + if (UsageValue.u.Extended > LocalItemState->UsageMaximum.u.Extended) + { + // + // maximum reached + // + UsageValue.u.Extended = LocalItemState->UsageMaximum.u.Extended; + } + } + + // + // usage usage bounds + // + UsageMinimum = UsageMaximum = UsageValue.u.Extended; + } + + // + // now store all values + // + ReportItem->ByteOffset = (Report->ReportSize / 8); + ReportItem->Shift = (Report->ReportSize % 8); + ReportItem->Mask = ~(0xFFFFFFFF << GlobalItemState->ReportSize); + ReportItem->BitCount = GlobalItemState->ReportSize; + ReportItem->HasData = (ItemData->DataConstant == FALSE); + ReportItem->Array = (ItemData->ArrayVariable == 0); + ReportItem->Relative = (ItemData->Relative == TRUE); + ReportItem->Minimum = LogicalMinimum; + ReportItem->Maximum = LogicalMaximum; + ReportItem->UsageMinimum = UsageMinimum; + ReportItem->UsageMaximum = UsageMaximum; + + // + // increment report size + // + Report->ReportSize += GlobalItemState->ReportSize; + + // + // completed successfully + // + return HIDPARSER_STATUS_SUCCESS; +} + +HIDPARSER_STATUS +HidParser_AddMainItem( + IN PHID_PARSER Parser, + IN PHID_REPORT Report, + IN PGLOBAL_ITEM_STATE GlobalItemState, + IN PLOCAL_ITEM_STATE LocalItemState, + IN PMAIN_ITEM_DATA ItemData, + IN PHID_COLLECTION Collection) +{ + PHID_REPORT_ITEM ReportItem; + HIDPARSER_STATUS Status; + ULONG Index; + + // + // first grow report item array + // + Status = HidParser_ReserveReportItems(Parser, Report, GlobalItemState->ReportCount); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // failed to allocate memory + // + return Status; + } + + // + // grow collection item array + // + Status = HidParser_ReserveCollectionItems(Parser, Collection, GlobalItemState->ReportCount); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // failed to allocate memory + // + return Status; + } + + + + for(Index = 0; Index < GlobalItemState->ReportCount; Index++) + { + // + // create report item + // + Status = HidParser_AllocateReportItem(Parser, &ReportItem); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // failed to allocate memory + // + return Status; + } + + Status = HidParser_InitReportItem(Report, ReportItem, GlobalItemState, LocalItemState, ItemData, Index); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // failed to init report item + // + return Status; + } + + // + // add report item + // + HidParser_AddReportItemToReport(Report, ReportItem); + HidParser_AddReportItemToCollection(Collection, ReportItem); + } + + // + // done + // + return HIDPARSER_STATUS_SUCCESS; +} + +HIDPARSER_STATUS +HidParser_ParseReportDescriptor( + IN PHID_PARSER Parser, + IN PUCHAR ReportDescriptor, + IN ULONG ReportLength) +{ + PGLOBAL_ITEM_STATE LinkedGlobalItemState, NextLinkedGlobalItemState; + ULONG Index; + PUSAGE_VALUE NewUsageStack, UsageValue; + HIDPARSER_STATUS Status; + PHID_COLLECTION CurrentCollection, NewCollection; + PUCHAR CurrentOffset, ReportEnd; + PITEM_PREFIX CurrentItem; + ULONG CurrentItemSize; + PLONG_ITEM CurrentLongItem; + PSHORT_ITEM CurrentShortItem; + ULONG Data; + UCHAR ReportType; + PHID_REPORT Report; + PMAIN_ITEM_DATA MainItemData; + PHID_PARSER_CONTEXT ParserContext; + + // + // reset parser + // + HidParser_ResetParser(Parser); + + // + // get parser context + // + ParserContext =(PHID_PARSER_CONTEXT)Parser->ParserContext; + ASSERT(ParserContext); + + // + // allocate usage stack + // + ParserContext->LocalItemState.UsageStackAllocated = 10; + ParserContext->LocalItemState.UsageStack = (PUSAGE_VALUE)Parser->Alloc(ParserContext->LocalItemState.UsageStackAllocated * sizeof(USAGE_VALUE)); + if (!ParserContext->LocalItemState.UsageStack) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // now allocate root collection + // + Status = HidParser_AllocateCollection(Parser, NULL, COLLECTION_LOGICAL, &ParserContext->LocalItemState, &ParserContext->RootCollection); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // no memory + // + Parser->Free(ParserContext->LocalItemState.UsageStack); + ParserContext->LocalItemState.UsageStack = NULL; + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // start parsing + // + CurrentCollection = ParserContext->RootCollection; + CurrentOffset = ReportDescriptor; + ReportEnd = ReportDescriptor + ReportLength; + + do + { + // + // get current item + // + CurrentItem = (PITEM_PREFIX)CurrentOffset; + + // + // get item size + // + CurrentItemSize = ItemSize[CurrentItem->Size]; + Data = 0; + + if (CurrentItem->Type == ITEM_TYPE_LONG) + { + // + // increment item size with size of data item + // + CurrentLongItem = (PLONG_ITEM)CurrentItem; + CurrentItemSize += CurrentLongItem->DataSize; + } + else + { + // + // get short item + // + CurrentShortItem = (PSHORT_ITEM)CurrentItem; + + // + // get associated data + // + //ASSERT(CurrentItemSize == 1 || CurrentItemSize == 2 || CurrentItemSize == 4); + if (CurrentItemSize == 1) + Data = CurrentShortItem->Data.UData8[0]; + else if (CurrentItemSize == 2) + Data = CurrentShortItem->Data.UData16[0]; + else if (CurrentItemSize == 4) + Data = CurrentShortItem->Data.UData32; + else + { + // + // invalid item size + // + //Parser->Debug("CurrentItem invalid item size %lu\n", CurrentItemSize); + } + + } + + // + // handle items + // + ASSERT(CurrentItem->Type >= ITEM_TYPE_MAIN && CurrentItem->Type <= ITEM_TYPE_LONG); + switch(CurrentItem->Type) + { + case ITEM_TYPE_MAIN: + { + // preprocess the local state if relevant (usages for + // collections and report items) + if (CurrentItem->Tag != ITEM_TAG_MAIN_END_COLLECTION) + { + // make all usages extended for easier later processing + for (Index = 0; Index < ParserContext->LocalItemState.UsageStackUsed; Index++) + { + // + // is it already extended + // + if (ParserContext->LocalItemState.UsageStack[Index].IsExtended) + continue; + + // + // extend usage item + // + ParserContext->LocalItemState.UsageStack[Index].u.s.UsagePage = ParserContext->GlobalItemState.UsagePage; + ParserContext->LocalItemState.UsageStack[Index].IsExtended = TRUE; + } + + if (!ParserContext->LocalItemState.UsageMinimum.IsExtended) { + // the specs say if one of them is extended they must + // both be extended, so if the minimum isn't, the + // maximum mustn't either. + ParserContext->LocalItemState.UsageMinimum.u.s.UsagePage + = ParserContext->LocalItemState.UsageMaximum.u.s.UsagePage + = ParserContext->GlobalItemState.UsagePage; + ParserContext->LocalItemState.UsageMinimum.IsExtended + = ParserContext->LocalItemState.UsageMaximum.IsExtended = TRUE; + } + + //LocalItemState.usage_stack = usageStack; + //ParserContext->LocalItemState.UsageStackUsed = UsageStackUsed; + } + + if (CurrentItem->Tag == ITEM_TAG_MAIN_COLLECTION) { + + // + // allocate new collection + // + Status = HidParser_AllocateCollection(Parser, CurrentCollection, (UCHAR)Data, &ParserContext->LocalItemState, &NewCollection); + ASSERT(Status == HIDPARSER_STATUS_SUCCESS); + + // + // add new collection to current collection + // + Status = HidParser_AddCollection(Parser, CurrentCollection, NewCollection); + ASSERT(Status == HIDPARSER_STATUS_SUCCESS); + + // + // make new collection current + // + CurrentCollection = NewCollection; + } + else if (CurrentItem->Tag == ITEM_TAG_MAIN_END_COLLECTION) + { + // + // assert on ending the root collection + // + ASSERT(CurrentCollection != ParserContext->RootCollection); + + // + // use parent of current collection + // + CurrentCollection = CurrentCollection->Root; + ASSERT(CurrentCollection); + } + else + { + ReportType = HID_REPORT_TYPE_ANY; + + switch (CurrentItem->Tag) { + case ITEM_TAG_MAIN_INPUT: + ReportType = HID_REPORT_TYPE_INPUT; + break; + + case ITEM_TAG_MAIN_OUTPUT: + ReportType = HID_REPORT_TYPE_OUTPUT; + break; + + case ITEM_TAG_MAIN_FEATURE: + ReportType = HID_REPORT_TYPE_FEATURE; + break; + + default: + Parser->Debug("[HIDPARSE] Unknown ReportType %x\n", CurrentItem->Tag); + ASSERT(FALSE); + break; + } + + if (ReportType == HID_REPORT_TYPE_ANY) + break; + + // + // get report + // + Status = HidParser_GetReport(Parser, ReportType, ParserContext->GlobalItemState.ReportId, TRUE, &Report); + ASSERT(Status == HIDPARSER_STATUS_SUCCESS); + + // fill in a sensible default if the index isn't set + if (!ParserContext->LocalItemState.DesignatorIndexSet) { + ParserContext->LocalItemState.DesignatorIndex + = ParserContext->LocalItemState.DesignatorMinimum; + } + + if (!ParserContext->LocalItemState.StringIndexSet) + ParserContext->LocalItemState.StringIndex = ParserContext->LocalItemState.StringMinimum; + + // + // get main item data + // + MainItemData = (PMAIN_ITEM_DATA)&Data; + + // + // add states & data to the report + // + Status = HidParser_AddMainItem(Parser, Report, &ParserContext->GlobalItemState, &ParserContext->LocalItemState, MainItemData, CurrentCollection); + ASSERT(Status == HIDPARSER_STATUS_SUCCESS); + } + + // + // backup stack + // + Index = ParserContext->LocalItemState.UsageStackAllocated; + NewUsageStack = ParserContext->LocalItemState.UsageStack; + + // + // reset the local item state and clear the usage stack + // + Parser->Zero(&ParserContext->LocalItemState, sizeof(LOCAL_ITEM_STATE)); + + // + // restore stack + // + ParserContext->LocalItemState.UsageStack = NewUsageStack; + ParserContext->LocalItemState.UsageStackAllocated = Index; + break; + } + case ITEM_TYPE_GLOBAL: + { + switch (CurrentItem->Tag) { + case ITEM_TAG_GLOBAL_USAGE_PAGE: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_USAGE_PAGE %x\n", Data); + ParserContext->GlobalItemState.UsagePage = Data; + break; + case ITEM_TAG_GLOBAL_LOGICAL_MINIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_LOGICAL_MINIMUM %x\n", Data); + ParserContext->GlobalItemState.LogicalMinimum = Data; + break; + + case ITEM_TAG_GLOBAL_LOGICAL_MAXIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_LOCAL_MAXIMUM %x\n", Data); + ParserContext->GlobalItemState.LogicialMaximum = Data; + break; + + case ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM %x\n", Data); + ParserContext->GlobalItemState.PhysicalMinimum = Data; + break; + + case ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM %x\n", Data); + ParserContext->GlobalItemState.PhysicalMaximum = Data; + break; + + case ITEM_TAG_GLOBAL_UNIT_EXPONENT: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_UNIT_EXPONENT %x\n", Data); + ParserContext->GlobalItemState.UnitExponent = Data; + break; + + case ITEM_TAG_GLOBAL_UNIT: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_UNIT %x\n", Data); + ParserContext->GlobalItemState.Unit = Data; + break; + + case ITEM_TAG_GLOBAL_REPORT_SIZE: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_SIZE %x\n", Data); + ParserContext->GlobalItemState.ReportSize = Data; + break; + + case ITEM_TAG_GLOBAL_REPORT_ID: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_ID %x\n", Data); + ParserContext->GlobalItemState.ReportId = Data; + ParserContext->UseReportIDs = TRUE; + break; + + case ITEM_TAG_GLOBAL_REPORT_COUNT: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_COUNT %x\n", Data); + ParserContext->GlobalItemState.ReportCount = Data; + break; + + case ITEM_TAG_GLOBAL_PUSH: + { + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_PUSH\n"); + // + // allocate global item state + // + LinkedGlobalItemState = (PGLOBAL_ITEM_STATE)Parser->Alloc(sizeof(GLOBAL_ITEM_STATE)); + ASSERT(LinkedGlobalItemState); + + // + // copy global item state + // + Parser->Copy(LinkedGlobalItemState, &ParserContext->GlobalItemState, sizeof(GLOBAL_ITEM_STATE)); + + // + // store pushed item in link member + // + ParserContext->GlobalItemState.Next = (struct __GLOBAL_ITEM_STATE__*)LinkedGlobalItemState; + break; + } + case ITEM_TAG_GLOBAL_POP: + { + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_POP\n"); + if (ParserContext->GlobalItemState.Next == NULL) + { + // + // pop without push + // + ASSERT(FALSE); + break; + } + + // + // get link + // + LinkedGlobalItemState = (PGLOBAL_ITEM_STATE)ParserContext->GlobalItemState.Next; + + // + // replace current item with linked one + // + Parser->Copy(&ParserContext->GlobalItemState, LinkedGlobalItemState, sizeof(GLOBAL_ITEM_STATE)); + + // + // free item + // + Parser->Free(LinkedGlobalItemState); + break; + } + + default: + // + // unknown / unsupported tag + // + ASSERT(FALSE); + break; + } + + break; + } + case ITEM_TYPE_LOCAL: + { + switch (CurrentItem->Tag) + { + case ITEM_TAG_LOCAL_USAGE: + { + if (ParserContext->LocalItemState.UsageStackUsed >= ParserContext->LocalItemState.UsageStackAllocated) + { + // + // increment stack size + // + ParserContext->LocalItemState.UsageStackAllocated += 10; + + // + // build new usage stack + // + NewUsageStack = (PUSAGE_VALUE)Parser->Alloc(sizeof(USAGE_VALUE) * ParserContext->LocalItemState.UsageStackAllocated); + ASSERT(NewUsageStack); + + // + // copy old usage stack + // + Parser->Copy(NewUsageStack, ParserContext->LocalItemState.UsageStack, sizeof(USAGE_VALUE) * (ParserContext->LocalItemState.UsageStackAllocated - 10)); + + // + // free old usage stack + // + Parser->Free(ParserContext->LocalItemState.UsageStack); + + // + // replace with new usage stack + // + ParserContext->LocalItemState.UsageStack = NewUsageStack; + } + + // + // get fresh usage value + // + UsageValue = &ParserContext->LocalItemState.UsageStack[ParserContext->LocalItemState.UsageStackUsed]; + + // + // init usage stack + // + UsageValue->IsExtended = CurrentItemSize == sizeof(ULONG); + UsageValue->u.Extended = Data; + + // + // increment usage stack usage count + // + ParserContext->LocalItemState.UsageStackUsed++; + break; + } + + case ITEM_TAG_LOCAL_USAGE_MINIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_USAGE_MINIMUM Data %x\n", Data); + ParserContext->LocalItemState.UsageMinimum.u.Extended = Data; + ParserContext->LocalItemState.UsageMinimum.IsExtended + = CurrentItemSize == sizeof(ULONG); + ParserContext->LocalItemState.UsageMinimumSet = TRUE; + break; + + case ITEM_TAG_LOCAL_USAGE_MAXIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_USAGE_MAXIMUM Data %x\n", Data); + ParserContext->LocalItemState.UsageMaximum.u.Extended = Data; + ParserContext->LocalItemState.UsageMaximum.IsExtended + = CurrentItemSize == sizeof(ULONG); + ParserContext->LocalItemState.UsageMaximumSet = TRUE; + break; + + case ITEM_TAG_LOCAL_DESIGNATOR_INDEX: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_DESIGNATOR_INDEX Data %x\n", Data); + ParserContext->LocalItemState.DesignatorIndex = Data; + ParserContext->LocalItemState.DesignatorIndexSet = TRUE; + break; + + case ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM Data %x\n", Data); + ParserContext->LocalItemState.DesignatorMinimum = Data; + break; + + case ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM Data %x\n", Data); + ParserContext->LocalItemState.DesignatorMaximum = Data; + break; + + case ITEM_TAG_LOCAL_STRING_INDEX: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_STRING_INDEX Data %x\n", Data); + ParserContext->LocalItemState.StringIndex = Data; + ParserContext->LocalItemState.StringIndexSet = TRUE; + break; + + case ITEM_TAG_LOCAL_STRING_MINIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_STRING_MINIMUM Data %x\n", Data); + ParserContext->LocalItemState.StringMinimum = Data; + break; + + case ITEM_TAG_LOCAL_STRING_MAXIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_STRING_MAXIMUM Data %x\n", Data); + ParserContext->LocalItemState.StringMaximum = Data; + break; + + default: + Parser->Debug("Unknown Local Item Tag %x\n", CurrentItem->Tag); + ASSERT(FALSE); + break; + } + break; + } + + case ITEM_TYPE_LONG: + { + CurrentLongItem = (PLONG_ITEM)CurrentItem; + Parser->Debug("Unsupported ITEM_TYPE_LONG Tag %x\n", CurrentLongItem->LongItemTag); + break; + } + } + + // + // move to next item + // + CurrentOffset += CurrentItemSize + sizeof(ITEM_PREFIX); + + + }while(CurrentOffset < ReportEnd); + + + // + // cleanup global stack + // + LinkedGlobalItemState = (PGLOBAL_ITEM_STATE)ParserContext->GlobalItemState.Next; + while(LinkedGlobalItemState != NULL) + { + Parser->Debug("[HIDPARSE] Freeing GlobalState %p\n", LinkedGlobalItemState); + // + // free global item state + // + NextLinkedGlobalItemState = (PGLOBAL_ITEM_STATE)LinkedGlobalItemState->Next; + + // + // free state + // + Parser->Free(LinkedGlobalItemState); + + // + // move to next global state + // + LinkedGlobalItemState = NextLinkedGlobalItemState; + } + + // + // free usage stack + // + Parser->Free(ParserContext->LocalItemState.UsageStack); + ParserContext->LocalItemState.UsageStack = NULL; + + // + // done + // + return HIDPARSER_STATUS_SUCCESS; +} + diff --git a/lib/drivers/hidparser/parser.h b/lib/drivers/hidparser/parser.h new file mode 100644 index 00000000000..95b1050829f --- /dev/null +++ b/lib/drivers/hidparser/parser.h @@ -0,0 +1,362 @@ + +#pragma once +#include +#include +#include +#include "hidparser.h" + + /* + * Copyright 2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + +#define HID_REPORT_TYPE_ANY 0x07 + + +#define ITEM_TYPE_MAIN 0x0 +#define ITEM_TYPE_GLOBAL 0x1 +#define ITEM_TYPE_LOCAL 0x2 +#define ITEM_TYPE_LONG 0x3 + +#define ITEM_TAG_MAIN_INPUT 0x8 +#define ITEM_TAG_MAIN_OUTPUT 0x9 +#define ITEM_TAG_MAIN_FEATURE 0xb +#define ITEM_TAG_MAIN_COLLECTION 0xa +#define ITEM_TAG_MAIN_END_COLLECTION 0xc + +#define ITEM_TAG_GLOBAL_USAGE_PAGE 0x0 +#define ITEM_TAG_GLOBAL_LOGICAL_MINIMUM 0x1 +#define ITEM_TAG_GLOBAL_LOGICAL_MAXIMUM 0x2 +#define ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM 0x3 +#define ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM 0x4 +#define ITEM_TAG_GLOBAL_UNIT_EXPONENT 0x5 +#define ITEM_TAG_GLOBAL_UNIT 0x6 +#define ITEM_TAG_GLOBAL_REPORT_SIZE 0x7 +#define ITEM_TAG_GLOBAL_REPORT_ID 0x8 +#define ITEM_TAG_GLOBAL_REPORT_COUNT 0x9 +#define ITEM_TAG_GLOBAL_PUSH 0xa +#define ITEM_TAG_GLOBAL_POP 0xb + +#define ITEM_TAG_LOCAL_USAGE 0x0 +#define ITEM_TAG_LOCAL_USAGE_MINIMUM 0x1 +#define ITEM_TAG_LOCAL_USAGE_MAXIMUM 0x2 +#define ITEM_TAG_LOCAL_DESIGNATOR_INDEX 0x3 +#define ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM 0x4 +#define ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM 0x5 +#define ITEM_TAG_LOCAL_STRING_INDEX 0x7 +#define ITEM_TAG_LOCAL_STRING_MINIMUM 0x8 +#define ITEM_TAG_LOCAL_STRING_MAXIMUM 0x9 +#define ITEM_TAG_LOCAL_DELIMITER 0xa + +#define ITEM_TAG_LONG 0xf + +#define COLLECTION_PHYSICAL 0x00 +#define COLLECTION_APPLICATION 0x01 +#define COLLECTION_LOGICAL 0x02 +#define COLLECTION_REPORT 0x03 +#define COLLECTION_NAMED_ARRAY 0x04 +#define COLLECTION_USAGE_SWITCH 0x05 +#define COLLECTION_USAGE_MODIFIER 0x06 +#define COLLECTION_ALL 0xff + +#define UNIT_SYSTEM 0x0 +#define UNIT_LENGTH 0x1 +#define UNIT_MASS 0x2 +#define UNIT_TIME 0x3 +#define UNIT_TEMPERATURE 0x4 +#define UNIT_CURRENT 0x5 +#define UNIT_LUMINOUS_INTENSITY 0x6 + +#define USAGE_PAGE_SHIFT 16 +#define USAGE_PAGE_MASK 0xffff +#define USAGE_ID_SHIFT 0 +#define USAGE_ID_MASK 0xffff + +typedef struct +{ + UCHAR Size:2; + UCHAR Type:2; + UCHAR Tag:4; +}ITEM_PREFIX, *PITEM_PREFIX; + +typedef struct +{ + ITEM_PREFIX Prefix; + + union + { + UCHAR UData8[4]; + CHAR SData8[4]; + USHORT UData16[2]; + SHORT SData16[2]; + ULONG UData32; + LONG SData32; + }Data; + +}SHORT_ITEM, *PSHORT_ITEM; + +typedef struct +{ + ITEM_PREFIX Prefix; + UCHAR DataSize; + UCHAR LongItemTag; + UCHAR Data[0]; + +}LONG_ITEM,*PLONG_ITEM; + + +#define LBITFIELD9(b1,b2,b3,b4,b5,b6,b7,b8,b9) USHORT b9,b8,b7,b6,b5,b4,b3,b2,b1 +typedef struct +{ + USHORT DataConstant:1; + USHORT ArrayVariable:1; + USHORT Relative:1; + USHORT Wrap:1; + USHORT NonLinear:1; + USHORT NoPreferred:1; + USHORT NullState:1; + USHORT IsVolatile:1; + USHORT BitsBytes:1; + UCHAR reserved[2]; + +}MAIN_ITEM_DATA, *PMAIN_ITEM_DATA; + +typedef struct __GLOBAL_ITEM_STATE_ +{ + USHORT UsagePage; + ULONG LogicalMinimum; + ULONG LogicialMaximum; + ULONG PhysicalMinimum; + ULONG PhysicalMaximum; + UCHAR UnitExponent; + UCHAR Unit; + ULONG ReportSize; + ULONG ReportCount; + UCHAR ReportId; + struct __GLOBAL_ITEM_STATE__ * Next; +}GLOBAL_ITEM_STATE, *PGLOBAL_ITEM_STATE; + + +typedef struct usage_value +{ + union + { + struct { + USHORT UsageId; + USHORT UsagePage; + }s; + ULONG Extended; + }u; + + UCHAR IsExtended; +}USAGE_VALUE, *PUSAGE_VALUE; + + +typedef struct +{ + PUSAGE_VALUE UsageStack; + ULONG UsageStackUsed; + ULONG UsageStackAllocated; + + USAGE_VALUE UsageMinimum; + USAGE_VALUE UsageMaximum; + + UCHAR UsageMinimumSet; + UCHAR UsageMaximumSet; + + ULONG DesignatorIndex; + UCHAR DesignatorIndexSet; + + ULONG DesignatorMinimum; + ULONG DesignatorMaximum; + + UCHAR StringIndex; + UCHAR StringIndexSet; + UCHAR StringMinimum; + UCHAR StringMaximum; + +}LOCAL_ITEM_STATE, *PLOCAL_ITEM_STATE; + +typedef struct +{ + ULONG ByteOffset; + UCHAR Shift; + ULONG Mask; + UCHAR BitCount; + UCHAR HasData; + UCHAR Array; + UCHAR Relative; + ULONG Minimum; + ULONG Maximum; + ULONG UsageMinimum; + ULONG UsageMaximum; + ULONG Data; + UCHAR Valid; +}HID_REPORT_ITEM, *PHID_REPORT_ITEM; + +struct HID_REPORT; + +typedef struct __HID_COLLECTION__ +{ + struct __HID_COLLECTION__ * Root; + + UCHAR Type; + ULONG Usage; + UCHAR StringID; + UCHAR PhysicalID; + ULONG NodeCount; + struct __HID_COLLECTION__ ** Nodes; + + ULONG ItemCount; + ULONG ItemCountAllocated; + PHID_REPORT_ITEM * Items; + + //ULONG ReportCount; + //struct HID_REPORT ** Reports; + +}HID_COLLECTION, *PHID_COLLECTION; + +typedef struct +{ + UCHAR Type; + UCHAR ReportID; + ULONG ReportSize; + + ULONG ItemCount; + ULONG ItemAllocated; + PHID_REPORT_ITEM *Items; + + ULONG ReportStatus; + UCHAR * CurrentReport; + ULONG BusyCount; +}HID_REPORT, *PHID_REPORT; + +typedef struct +{ + // + // global item state + // + GLOBAL_ITEM_STATE GlobalItemState; + + // + // local item state + // + LOCAL_ITEM_STATE LocalItemState; + + // + // root collection + // + PHID_COLLECTION RootCollection; + + // + // report count + // + ULONG ReportCount; + + // + // reports + // + PHID_REPORT * Reports; + + // + // uses report ids + // + UCHAR UseReportIDs; + +}HID_PARSER_CONTEXT, *PHID_PARSER_CONTEXT; + +HIDPARSER_STATUS +HidParser_ParseReportDescriptor( + PHID_PARSER Parser, + PUCHAR Report, + ULONG ReportSize); + +ULONG +HidParser_NumberOfTopCollections( + IN PHID_PARSER Parser); + +#define HID_REPORT_TYPE_INPUT 0x01 +#define HID_REPORT_TYPE_OUTPUT 0x02 +#define HID_REPORT_TYPE_FEATURE 0x04 + +ULONG +HidParser_NumberOfReports( + IN PHID_PARSER Parser, + IN ULONG ReportType); + +HIDPARSER_STATUS +HidParser_GetCollectionUsagePage( + IN PHID_PARSER Parser, + IN ULONG CollectionNumber, + OUT PUSHORT Usage, + OUT PUSHORT UsagePage); + +ULONG +HidParser_GetReportLength( + IN PHID_PARSER Parser, + IN ULONG ReportType); + +UCHAR +HidParser_IsReportIDUsed( + IN PHID_PARSER Parser); + +ULONG +HidParser_GetReportItemCountFromReportType( + IN PHID_PARSER Parser, + IN ULONG ReportType); + +ULONG +HidParser_GetReportItemTypeCountFromReportType( + IN PHID_PARSER Parser, + IN ULONG ReportType, + IN ULONG bData); + +ULONG +HidParser_GetContextSize( + IN PHID_PARSER Parser); + +VOID +HidParser_FreeContext( + IN PHID_PARSER Parser, + IN PUCHAR Context, + IN ULONG ContextLength); + +ULONG +HidParser_GetTotalCollectionCount( + IN PHID_PARSER Parser); + +ULONG +HidParser_GetMaxUsageListLengthWithReportAndPage( + IN PHID_PARSER Parser, + IN ULONG ReportType, + IN USAGE UsagePage OPTIONAL); + +HIDPARSER_STATUS +HidParser_GetSpecificValueCapsWithReport( + IN PHID_PARSER Parser, + IN ULONG ReportType, + IN USHORT UsagePage, + IN USHORT Usage, + OUT PHIDP_VALUE_CAPS ValueCaps, + IN OUT PULONG ValueCapsLength); + + +HIDPARSER_STATUS +HidParser_GetUsagesWithReport( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + OUT USAGE *UsageList, + IN OUT PULONG UsageLength, + IN PCHAR ReportDescriptor, + IN ULONG ReportDescriptorLength); + +HIDPARSER_STATUS +HidParser_GetScaledUsageValueWithReport( + IN PHID_PARSER Parser, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USAGE Usage, + OUT PLONG UsageValue, + IN PCHAR ReportDescriptor, + IN ULONG ReportDescriptorLength); \ No newline at end of file