Index: kmtests/CMakeLists.txt =================================================================== --- kmtests/CMakeLists.txt (revision 71876) +++ kmtests/CMakeLists.txt (working copy) @@ -5,6 +5,7 @@ # subdirectories containing special-purpose drivers # add_subdirectory(example) +add_subdirectory(hidparse) add_subdirectory(kernel32) add_subdirectory(ntos_cc) add_subdirectory(ntos_io) @@ -119,6 +120,7 @@ kmtest/testlist.c example/Example_user.c + hidparse/HidP_user.c kernel32/FindFile_user.c ntos_cc/CcCopyRead_user.c ntos_io/IoCreateFile_user.c @@ -147,6 +149,7 @@ kmtest_drv example_drv findfile_drv + hidp_drv iocreatefile_drv iodeviceobject_drv iohelper_drv Index: kmtests/hidparse/CMakeLists.txt =================================================================== --- kmtests/hidparse/CMakeLists.txt (nonexistent) +++ kmtests/hidparse/CMakeLists.txt (working copy) @@ -0,0 +1,18 @@ + +include_directories(../include) + +# +# HidP +# +list(APPEND HIDP_DRV_SOURCE + ../kmtest_drv/kmtest_standalone.c + HidP_drv.c + HidPDescription.c) + +add_library(hidp_drv SHARED ${HIDP_DRV_SOURCE}) +set_module_type(hidp_drv kernelmodedriver) +target_link_libraries(hidp_drv kmtest_printf ${PSEH_LIB}) +add_importlibs(hidp_drv hidparse ntoskrnl hal) +add_target_compile_definitions(hidp_drv KMT_STANDALONE_DRIVER) +#add_pch(hidp_drv ../include/kmt_test.h) +add_cd_file(TARGET hidp_drv DESTINATION reactos/bin FOR all) Index: kmtests/hidparse/HidP.h =================================================================== --- kmtests/hidparse/HidP.h (nonexistent) +++ kmtests/hidparse/HidP.h (working copy) @@ -0,0 +1,13 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory + * PURPOSE: HidParser test declarations + * PROGRAMMER: Thomas Faber + */ + +#ifndef _KMTEST_HIDP_H_ +#define _KMTEST_HIDP_H_ + +#define IOCTL_TEST_DESCRIPTION 1 + +#endif /* !defined _KMTEST_HIDP_H_ */ Index: kmtests/hidparse/HidPDescription.c =================================================================== --- kmtests/hidparse/HidPDescription.c (nonexistent) +++ kmtests/hidparse/HidPDescription.c (working copy) @@ -0,0 +1,258 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory + * PURPOSE: HidParser description test + * PROGRAMMER: Thomas Faber + */ + +#include +#include + +#define NDEBUG +#include + +#include "HidP.h" + +static UCHAR ExampleKeyboardDescriptor[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop), */ + 0x09, 0x06, /* Usage (Keyboard), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x05, 0x07, /* Usage Page (Key Codes); */ + 0x19, 0xE0, /* Usage Minimum (224), */ + 0x29, 0xE7, /* Usage Maximum (231), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x02, /* Input (Data, Variable, Absolute), ;Modifier byte */ + 0x95, 0x01, /* Report Count (1), */ + 0x75, 0x08, /* Report Size (8), */ + 0x81, 0x01, /* Input (Constant), ;Reserved byte */ + 0x95, 0x05, /* Report Count (5), */ + 0x75, 0x01, /* Report Size (1), */ + 0x05, 0x08, /* Usage Page (Page# for LEDs), */ + 0x19, 0x01, /* Usage Minimum (1), */ + 0x29, 0x05, /* Usage Maximum (5), */ + 0x91, 0x02, /* Output (Data, Variable, Absolute), ;LED report */ + 0x95, 0x01, /* Report Count (1), */ + 0x75, 0x03, /* Report Size (3), */ + 0x91, 0x01, /* Output (Constant), ;LED report padding */ + 0x95, 0x06, /* Report Count (6), */ + 0x75, 0x08, /* Report Size (8), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x65, /* Logical Maximum (101), */ + 0x05, 0x07, /* Usage Page (Key Codes), */ + 0x19, 0x00, /* Usage Minimum (0), */ + 0x29, 0x65, /* Usage Maximum (101) */ + 0x81, 0x00, /* Input (Data, Array), ;Key arrays (6 bytes) */ + 0xC0 /* End Collection */ +}; + +static UCHAR PowerProEliteDescriptor[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop), */ + 0x09, 0x04, /* Usage (Joystick), */ + 0xa1, 0x01, /* Collection (Application), */ + 0xa1, 0x02, /* Collection (Logical), */ + 0x85, 0x01, /* Report ID (1) */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xff, 0x00, /* Logical Maximum (255), */ + 0x81, 0x03, /* Input (Constant, Variable, Absolute), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x13, /* Report Count (19), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x35, 0x00, /* Physical Minimum (0), */ + 0x45, 0x01, /* Physical Maximum (1), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (1), */ + 0x29, 0x13, /* Usage Maximum (19), */ + 0x81, 0x02, /* Input (Data, Variable, Absolute), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x0d, /* Report Count (13), */ + 0x06, 0x00, 0xff, /* Usage Page (Vendor-defined FF00), */ + 0x81, 0x03, /* Input (Constant, Variable, Absolute), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xff, 0x00, /* Logical Maximum (255), */ + 0x05, 0x01, /* Usage Page (Generic Desktop), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xa1, 0x00, /* Collection (Physical), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x04, /* Report Count (4), */ + 0x35, 0x00, /* Physical Minimum (0), */ + 0x46, 0xff, 0x00, /* Physical Maximum (255), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x09, 0x32, /* Usage (Z), */ + 0x09, 0x35, /* Usage (Rz), */ + 0x81, 0x02, /* Input (Data, Variable, Absolute), */ + 0xc0, /* End Collection */ + 0x05, 0x01, /* Usage Page (Generic Desktop), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x27, /* Report Count (39), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0x81, 0x02, /* Input (Data, Variable, Absolute), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0x91, 0x02, /* Output (Data, Variable, Absolute), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xb1, 0x02, /* Feature (Data, Variable, Absolute), */ + 0xc0, /* End Collection */ + + 0xa1, 0x02, /* Collection (Logical), */ + 0x85, 0x02, /* Report ID (2) */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xb1, 0x02, /* Feature (Data, Variable, Absolute), */ + 0xc0, /* End Collection */ + 0xa1, 0x02, /* Collection (Logical), */ + 0x85, 0xee, /* Report ID (238) */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xb1, 0x02, /* Feature (Data, Variable, Absolute), */ + 0xc0, /* End Collection */ + 0xa1, 0x02, /* Collection (Logical), */ + 0x85, 0xef, /* Report ID (239) */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xb1, 0x02, /* Feature (Data, Variable, Absolute), */ + 0xc0, /* End Collection */ + 0xc0, /* End Collection */ +}; +C_ASSERT(sizeof(PowerProEliteDescriptor) == 148); + +static +VOID +TestGetCollectionDescription(VOID) +{ + NTSTATUS Status; + HIDP_DEVICE_DESC DeviceDescription; + + /* Empty report descriptor */ + RtlFillMemory(&DeviceDescription, sizeof(DeviceDescription), 0x55); + Status = HidP_GetCollectionDescription(NULL, + 0, + NonPagedPool, + &DeviceDescription); + ok_eq_hex(Status, STATUS_NO_DATA_DETECTED); + ok_eq_pointer(DeviceDescription.CollectionDesc, NULL); + ok_eq_ulong(DeviceDescription.CollectionDescLength, 0); + ok_eq_pointer(DeviceDescription.ReportIDs, NULL); + ok_eq_ulong(DeviceDescription.ReportIDsLength, 0); + if (NT_SUCCESS(Status)) HidP_FreeCollectionDescription(&DeviceDescription); + + /* Sample keyboard report descriptor from the HID spec */ + Status = HidP_GetCollectionDescription(ExampleKeyboardDescriptor, + sizeof(ExampleKeyboardDescriptor), + NonPagedPool, + &DeviceDescription); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_ulong(DeviceDescription.CollectionDescLength, 1); + ok_eq_ulong(DeviceDescription.ReportIDsLength, 1); + if (!skip(NT_SUCCESS(Status), "Parsing failure\n")) + { + if (!skip(DeviceDescription.CollectionDescLength >= 1, "No collection\n")) + { + ok_eq_uint(DeviceDescription.CollectionDesc[0].UsagePage, HID_USAGE_PAGE_GENERIC); + ok_eq_uint(DeviceDescription.CollectionDesc[0].Usage, HID_USAGE_GENERIC_KEYBOARD); + ok_eq_uint(DeviceDescription.CollectionDesc[0].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.CollectionDesc[0].InputLength, 9); + ok_eq_uint(DeviceDescription.CollectionDesc[0].OutputLength, 2); + ok_eq_uint(DeviceDescription.CollectionDesc[0].FeatureLength, 0); + ok_eq_uint(DeviceDescription.CollectionDesc[0].PreparsedDataLength, 476); + } + if (!skip(DeviceDescription.ReportIDsLength >= 1, "No report IDs\n")) + { + ok_eq_uint(DeviceDescription.ReportIDs[0].ReportID, 0); + ok_eq_uint(DeviceDescription.ReportIDs[0].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.ReportIDs[0].InputLength, 8); + ok_eq_uint(DeviceDescription.ReportIDs[0].OutputLength, 1); + ok_eq_uint(DeviceDescription.ReportIDs[0].FeatureLength, 0); + } + HidP_FreeCollectionDescription(&DeviceDescription); + } + + /* Regression test for CORE-11538 */ + Status = HidP_GetCollectionDescription(PowerProEliteDescriptor, + sizeof(PowerProEliteDescriptor), + NonPagedPool, + &DeviceDescription); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_ulong(DeviceDescription.CollectionDescLength, 1); + ok_eq_ulong(DeviceDescription.ReportIDsLength, 4); + if (!skip(NT_SUCCESS(Status), "Parsing failure\n")) + { + if (!skip(DeviceDescription.CollectionDescLength >= 1, "No collection\n")) + { + ok_eq_uint(DeviceDescription.CollectionDesc[0].UsagePage, HID_USAGE_PAGE_GENERIC); + ok_eq_uint(DeviceDescription.CollectionDesc[0].Usage, HID_USAGE_GENERIC_JOYSTICK); + ok_eq_uint(DeviceDescription.CollectionDesc[0].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.CollectionDesc[0].InputLength, 49); + ok_eq_uint(DeviceDescription.CollectionDesc[0].OutputLength, 49); + ok_eq_uint(DeviceDescription.CollectionDesc[0].FeatureLength, 49); + ok_eq_uint(DeviceDescription.CollectionDesc[0].PreparsedDataLength, 1388); + } + if (!skip(DeviceDescription.ReportIDsLength >= 1, "No first report ID\n")) + { + ok_eq_uint(DeviceDescription.ReportIDs[0].ReportID, 1); + ok_eq_uint(DeviceDescription.ReportIDs[0].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.ReportIDs[0].InputLength, 49); + ok_eq_uint(DeviceDescription.ReportIDs[0].OutputLength, 49); + ok_eq_uint(DeviceDescription.ReportIDs[0].FeatureLength, 49); + } + if (!skip(DeviceDescription.ReportIDsLength >= 2, "No second report ID\n")) + { + ok_eq_uint(DeviceDescription.ReportIDs[1].ReportID, 2); + ok_eq_uint(DeviceDescription.ReportIDs[1].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.ReportIDs[1].InputLength, 0); + ok_eq_uint(DeviceDescription.ReportIDs[1].OutputLength, 0); + ok_eq_uint(DeviceDescription.ReportIDs[1].FeatureLength, 49); + } + if (!skip(DeviceDescription.ReportIDsLength >= 3, "No third report ID\n")) + { + ok_eq_uint(DeviceDescription.ReportIDs[2].ReportID, 238); + ok_eq_uint(DeviceDescription.ReportIDs[2].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.ReportIDs[2].InputLength, 0); + ok_eq_uint(DeviceDescription.ReportIDs[2].OutputLength, 0); + ok_eq_uint(DeviceDescription.ReportIDs[2].FeatureLength, 49); + } + if (!skip(DeviceDescription.ReportIDsLength >= 4, "No fourth report ID\n")) + { + ok_eq_uint(DeviceDescription.ReportIDs[3].ReportID, 239); + ok_eq_uint(DeviceDescription.ReportIDs[3].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.ReportIDs[3].InputLength, 0); + ok_eq_uint(DeviceDescription.ReportIDs[3].OutputLength, 0); + ok_eq_uint(DeviceDescription.ReportIDs[3].FeatureLength, 49); + } + HidP_FreeCollectionDescription(&DeviceDescription); + } +} + +NTSTATUS +TestHidPDescription( + IN PDEVICE_OBJECT DeviceObject, + IN ULONG ControlCode, + IN PVOID Buffer OPTIONAL, + IN SIZE_T InLength, + IN OUT PSIZE_T OutLength) +{ + UNREFERENCED_PARAMETER(DeviceObject); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(InLength); + UNREFERENCED_PARAMETER(OutLength); + + PAGED_CODE(); + + NT_VERIFY(ControlCode == IOCTL_TEST_DESCRIPTION); + + TestGetCollectionDescription(); + + return STATUS_SUCCESS; +} \ No newline at end of file Index: kmtests/hidparse/HidP_drv.c =================================================================== --- kmtests/hidparse/HidP_drv.c (nonexistent) +++ kmtests/hidparse/HidP_drv.c (working copy) @@ -0,0 +1,41 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory + * PURPOSE: Test driver for HidParser functionality + * PROGRAMMER: Thomas Faber + */ + +#include + +#define NDEBUG +#include + +#include "HidP.h" + +KMT_MESSAGE_HANDLER TestHidPDescription; + +NTSTATUS +TestEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PCUNICODE_STRING RegistryPath, + _Out_ PCWSTR *DeviceName, + _Inout_ INT *Flags) +{ + UNREFERENCED_PARAMETER(RegistryPath); + + PAGED_CODE(); + + *DeviceName = L"HidP"; + *Flags = TESTENTRY_NO_EXCLUSIVE_DEVICE; + + KmtRegisterMessageHandler(IOCTL_TEST_DESCRIPTION, NULL, TestHidPDescription); + + return STATUS_SUCCESS; +} + +VOID +TestUnload( + _In_ PDRIVER_OBJECT DriverObject) +{ + PAGED_CODE(); +} Index: kmtests/hidparse/HidP_user.c =================================================================== --- kmtests/hidparse/HidP_user.c (nonexistent) +++ kmtests/hidparse/HidP_user.c (working copy) @@ -0,0 +1,23 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: GPLv2+ - See COPYING in the top level directory + * PURPOSE: Kernel-Mode Test Suite Driver Object test user-mode part + * PROGRAMMER: Thomas Faber + */ + +#include +#include "HidP.h" + +START_TEST(HidPDescription) +{ + DWORD Error; + + KmtLoadDriver(L"HidP", FALSE); + KmtOpenDriver(); + + Error = KmtSendToDriver(IOCTL_TEST_DESCRIPTION); + ok(Error == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %lx\n", Error); + + KmtCloseDriver(); + KmtUnloadDriver(); +} Index: kmtests/hidparse/CMakeLists.txt =================================================================== --- kmtests/hidparse/CMakeLists.txt (nonexistent) +++ kmtests/hidparse/CMakeLists.txt (working copy) @@ -0,0 +1,18 @@ + +include_directories(../include) + +# +# HidP +# +list(APPEND HIDP_DRV_SOURCE + ../kmtest_drv/kmtest_standalone.c + HidP_drv.c + HidPDescription.c) + +add_library(hidp_drv SHARED ${HIDP_DRV_SOURCE}) +set_module_type(hidp_drv kernelmodedriver) +target_link_libraries(hidp_drv kmtest_printf ${PSEH_LIB}) +add_importlibs(hidp_drv hidparse ntoskrnl hal) +add_target_compile_definitions(hidp_drv KMT_STANDALONE_DRIVER) +#add_pch(hidp_drv ../include/kmt_test.h) +add_cd_file(TARGET hidp_drv DESTINATION reactos/bin FOR all) Index: kmtests/hidparse/HidP.h =================================================================== --- kmtests/hidparse/HidP.h (nonexistent) +++ kmtests/hidparse/HidP.h (working copy) @@ -0,0 +1,13 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory + * PURPOSE: HidParser test declarations + * PROGRAMMER: Thomas Faber + */ + +#ifndef _KMTEST_HIDP_H_ +#define _KMTEST_HIDP_H_ + +#define IOCTL_TEST_DESCRIPTION 1 + +#endif /* !defined _KMTEST_HIDP_H_ */ Index: kmtests/hidparse/HidP_drv.c =================================================================== --- kmtests/hidparse/HidP_drv.c (nonexistent) +++ kmtests/hidparse/HidP_drv.c (working copy) @@ -0,0 +1,41 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory + * PURPOSE: Test driver for HidParser functionality + * PROGRAMMER: Thomas Faber + */ + +#include + +#define NDEBUG +#include + +#include "HidP.h" + +KMT_MESSAGE_HANDLER TestHidPDescription; + +NTSTATUS +TestEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PCUNICODE_STRING RegistryPath, + _Out_ PCWSTR *DeviceName, + _Inout_ INT *Flags) +{ + UNREFERENCED_PARAMETER(RegistryPath); + + PAGED_CODE(); + + *DeviceName = L"HidP"; + *Flags = TESTENTRY_NO_EXCLUSIVE_DEVICE; + + KmtRegisterMessageHandler(IOCTL_TEST_DESCRIPTION, NULL, TestHidPDescription); + + return STATUS_SUCCESS; +} + +VOID +TestUnload( + _In_ PDRIVER_OBJECT DriverObject) +{ + PAGED_CODE(); +} Index: kmtests/hidparse/HidP_user.c =================================================================== --- kmtests/hidparse/HidP_user.c (nonexistent) +++ kmtests/hidparse/HidP_user.c (working copy) @@ -0,0 +1,23 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: GPLv2+ - See COPYING in the top level directory + * PURPOSE: Kernel-Mode Test Suite Driver Object test user-mode part + * PROGRAMMER: Thomas Faber + */ + +#include +#include "HidP.h" + +START_TEST(HidPDescription) +{ + DWORD Error; + + KmtLoadDriver(L"HidP", FALSE); + KmtOpenDriver(); + + Error = KmtSendToDriver(IOCTL_TEST_DESCRIPTION); + ok(Error == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %lx\n", Error); + + KmtCloseDriver(); + KmtUnloadDriver(); +} Index: kmtests/hidparse/HidPDescription.c =================================================================== --- kmtests/hidparse/HidPDescription.c (nonexistent) +++ kmtests/hidparse/HidPDescription.c (working copy) @@ -0,0 +1,258 @@ +/* + * PROJECT: ReactOS kernel-mode tests + * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory + * PURPOSE: HidParser description test + * PROGRAMMER: Thomas Faber + */ + +#include +#include + +#define NDEBUG +#include + +#include "HidP.h" + +static UCHAR ExampleKeyboardDescriptor[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop), */ + 0x09, 0x06, /* Usage (Keyboard), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x05, 0x07, /* Usage Page (Key Codes); */ + 0x19, 0xE0, /* Usage Minimum (224), */ + 0x29, 0xE7, /* Usage Maximum (231), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x02, /* Input (Data, Variable, Absolute), ;Modifier byte */ + 0x95, 0x01, /* Report Count (1), */ + 0x75, 0x08, /* Report Size (8), */ + 0x81, 0x01, /* Input (Constant), ;Reserved byte */ + 0x95, 0x05, /* Report Count (5), */ + 0x75, 0x01, /* Report Size (1), */ + 0x05, 0x08, /* Usage Page (Page# for LEDs), */ + 0x19, 0x01, /* Usage Minimum (1), */ + 0x29, 0x05, /* Usage Maximum (5), */ + 0x91, 0x02, /* Output (Data, Variable, Absolute), ;LED report */ + 0x95, 0x01, /* Report Count (1), */ + 0x75, 0x03, /* Report Size (3), */ + 0x91, 0x01, /* Output (Constant), ;LED report padding */ + 0x95, 0x06, /* Report Count (6), */ + 0x75, 0x08, /* Report Size (8), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x65, /* Logical Maximum (101), */ + 0x05, 0x07, /* Usage Page (Key Codes), */ + 0x19, 0x00, /* Usage Minimum (0), */ + 0x29, 0x65, /* Usage Maximum (101) */ + 0x81, 0x00, /* Input (Data, Array), ;Key arrays (6 bytes) */ + 0xC0 /* End Collection */ +}; + +static UCHAR PowerProEliteDescriptor[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop), */ + 0x09, 0x04, /* Usage (Joystick), */ + 0xa1, 0x01, /* Collection (Application), */ + 0xa1, 0x02, /* Collection (Logical), */ + 0x85, 0x01, /* Report ID (1) */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xff, 0x00, /* Logical Maximum (255), */ + 0x81, 0x03, /* Input (Constant, Variable, Absolute), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x13, /* Report Count (19), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x35, 0x00, /* Physical Minimum (0), */ + 0x45, 0x01, /* Physical Maximum (1), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (1), */ + 0x29, 0x13, /* Usage Maximum (19), */ + 0x81, 0x02, /* Input (Data, Variable, Absolute), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x0d, /* Report Count (13), */ + 0x06, 0x00, 0xff, /* Usage Page (Vendor-defined FF00), */ + 0x81, 0x03, /* Input (Constant, Variable, Absolute), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xff, 0x00, /* Logical Maximum (255), */ + 0x05, 0x01, /* Usage Page (Generic Desktop), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xa1, 0x00, /* Collection (Physical), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x04, /* Report Count (4), */ + 0x35, 0x00, /* Physical Minimum (0), */ + 0x46, 0xff, 0x00, /* Physical Maximum (255), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x09, 0x32, /* Usage (Z), */ + 0x09, 0x35, /* Usage (Rz), */ + 0x81, 0x02, /* Input (Data, Variable, Absolute), */ + 0xc0, /* End Collection */ + 0x05, 0x01, /* Usage Page (Generic Desktop), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x27, /* Report Count (39), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0x81, 0x02, /* Input (Data, Variable, Absolute), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0x91, 0x02, /* Output (Data, Variable, Absolute), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xb1, 0x02, /* Feature (Data, Variable, Absolute), */ + 0xc0, /* End Collection */ + + 0xa1, 0x02, /* Collection (Logical), */ + 0x85, 0x02, /* Report ID (2) */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xb1, 0x02, /* Feature (Data, Variable, Absolute), */ + 0xc0, /* End Collection */ + 0xa1, 0x02, /* Collection (Logical), */ + 0x85, 0xee, /* Report ID (238) */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xb1, 0x02, /* Feature (Data, Variable, Absolute), */ + 0xc0, /* End Collection */ + 0xa1, 0x02, /* Collection (Logical), */ + 0x85, 0xef, /* Report ID (239) */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xb1, 0x02, /* Feature (Data, Variable, Absolute), */ + 0xc0, /* End Collection */ + 0xc0, /* End Collection */ +}; +C_ASSERT(sizeof(PowerProEliteDescriptor) == 148); + +static +VOID +TestGetCollectionDescription(VOID) +{ + NTSTATUS Status; + HIDP_DEVICE_DESC DeviceDescription; + + /* Empty report descriptor */ + RtlFillMemory(&DeviceDescription, sizeof(DeviceDescription), 0x55); + Status = HidP_GetCollectionDescription(NULL, + 0, + NonPagedPool, + &DeviceDescription); + ok_eq_hex(Status, STATUS_NO_DATA_DETECTED); + ok_eq_pointer(DeviceDescription.CollectionDesc, NULL); + ok_eq_ulong(DeviceDescription.CollectionDescLength, 0); + ok_eq_pointer(DeviceDescription.ReportIDs, NULL); + ok_eq_ulong(DeviceDescription.ReportIDsLength, 0); + if (NT_SUCCESS(Status)) HidP_FreeCollectionDescription(&DeviceDescription); + + /* Sample keyboard report descriptor from the HID spec */ + Status = HidP_GetCollectionDescription(ExampleKeyboardDescriptor, + sizeof(ExampleKeyboardDescriptor), + NonPagedPool, + &DeviceDescription); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_ulong(DeviceDescription.CollectionDescLength, 1); + ok_eq_ulong(DeviceDescription.ReportIDsLength, 1); + if (!skip(NT_SUCCESS(Status), "Parsing failure\n")) + { + if (!skip(DeviceDescription.CollectionDescLength >= 1, "No collection\n")) + { + ok_eq_uint(DeviceDescription.CollectionDesc[0].UsagePage, HID_USAGE_PAGE_GENERIC); + ok_eq_uint(DeviceDescription.CollectionDesc[0].Usage, HID_USAGE_GENERIC_KEYBOARD); + ok_eq_uint(DeviceDescription.CollectionDesc[0].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.CollectionDesc[0].InputLength, 9); + ok_eq_uint(DeviceDescription.CollectionDesc[0].OutputLength, 2); + ok_eq_uint(DeviceDescription.CollectionDesc[0].FeatureLength, 0); + ok_eq_uint(DeviceDescription.CollectionDesc[0].PreparsedDataLength, 476); + } + if (!skip(DeviceDescription.ReportIDsLength >= 1, "No report IDs\n")) + { + ok_eq_uint(DeviceDescription.ReportIDs[0].ReportID, 0); + ok_eq_uint(DeviceDescription.ReportIDs[0].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.ReportIDs[0].InputLength, 8); + ok_eq_uint(DeviceDescription.ReportIDs[0].OutputLength, 1); + ok_eq_uint(DeviceDescription.ReportIDs[0].FeatureLength, 0); + } + HidP_FreeCollectionDescription(&DeviceDescription); + } + + /* Regression test for CORE-11538 */ + Status = HidP_GetCollectionDescription(PowerProEliteDescriptor, + sizeof(PowerProEliteDescriptor), + NonPagedPool, + &DeviceDescription); + ok_eq_hex(Status, STATUS_SUCCESS); + ok_eq_ulong(DeviceDescription.CollectionDescLength, 1); + ok_eq_ulong(DeviceDescription.ReportIDsLength, 4); + if (!skip(NT_SUCCESS(Status), "Parsing failure\n")) + { + if (!skip(DeviceDescription.CollectionDescLength >= 1, "No collection\n")) + { + ok_eq_uint(DeviceDescription.CollectionDesc[0].UsagePage, HID_USAGE_PAGE_GENERIC); + ok_eq_uint(DeviceDescription.CollectionDesc[0].Usage, HID_USAGE_GENERIC_JOYSTICK); + ok_eq_uint(DeviceDescription.CollectionDesc[0].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.CollectionDesc[0].InputLength, 49); + ok_eq_uint(DeviceDescription.CollectionDesc[0].OutputLength, 49); + ok_eq_uint(DeviceDescription.CollectionDesc[0].FeatureLength, 49); + ok_eq_uint(DeviceDescription.CollectionDesc[0].PreparsedDataLength, 1388); + } + if (!skip(DeviceDescription.ReportIDsLength >= 1, "No first report ID\n")) + { + ok_eq_uint(DeviceDescription.ReportIDs[0].ReportID, 1); + ok_eq_uint(DeviceDescription.ReportIDs[0].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.ReportIDs[0].InputLength, 49); + ok_eq_uint(DeviceDescription.ReportIDs[0].OutputLength, 49); + ok_eq_uint(DeviceDescription.ReportIDs[0].FeatureLength, 49); + } + if (!skip(DeviceDescription.ReportIDsLength >= 2, "No second report ID\n")) + { + ok_eq_uint(DeviceDescription.ReportIDs[1].ReportID, 2); + ok_eq_uint(DeviceDescription.ReportIDs[1].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.ReportIDs[1].InputLength, 0); + ok_eq_uint(DeviceDescription.ReportIDs[1].OutputLength, 0); + ok_eq_uint(DeviceDescription.ReportIDs[1].FeatureLength, 49); + } + if (!skip(DeviceDescription.ReportIDsLength >= 3, "No third report ID\n")) + { + ok_eq_uint(DeviceDescription.ReportIDs[2].ReportID, 238); + ok_eq_uint(DeviceDescription.ReportIDs[2].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.ReportIDs[2].InputLength, 0); + ok_eq_uint(DeviceDescription.ReportIDs[2].OutputLength, 0); + ok_eq_uint(DeviceDescription.ReportIDs[2].FeatureLength, 49); + } + if (!skip(DeviceDescription.ReportIDsLength >= 4, "No fourth report ID\n")) + { + ok_eq_uint(DeviceDescription.ReportIDs[3].ReportID, 239); + ok_eq_uint(DeviceDescription.ReportIDs[3].CollectionNumber, 1); + ok_eq_uint(DeviceDescription.ReportIDs[3].InputLength, 0); + ok_eq_uint(DeviceDescription.ReportIDs[3].OutputLength, 0); + ok_eq_uint(DeviceDescription.ReportIDs[3].FeatureLength, 49); + } + HidP_FreeCollectionDescription(&DeviceDescription); + } +} + +NTSTATUS +TestHidPDescription( + IN PDEVICE_OBJECT DeviceObject, + IN ULONG ControlCode, + IN PVOID Buffer OPTIONAL, + IN SIZE_T InLength, + IN OUT PSIZE_T OutLength) +{ + UNREFERENCED_PARAMETER(DeviceObject); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(InLength); + UNREFERENCED_PARAMETER(OutLength); + + PAGED_CODE(); + + NT_VERIFY(ControlCode == IOCTL_TEST_DESCRIPTION); + + TestGetCollectionDescription(); + + return STATUS_SUCCESS; +} \ No newline at end of file