4815 lines
148 KiB
C
4815 lines
148 KiB
C
#define __HIDTEST_C__
|
|
|
|
/*
|
|
// This file contains the code used for performing different tests on the Hid
|
|
// Devices. A Hid Device can be either a physical or logical device where
|
|
// a physical device is considered a tangible object like a mouse or keyboard.
|
|
// A logical device is simply Preparsed Data that has been generated from a
|
|
// report descriptor (either manually created or captured from a physical
|
|
// device. Therefore, the test APIs that apply to logical devices are more
|
|
// limited since you cannot open a file object up for them.
|
|
//
|
|
// Since I've had problems trying to decide what my test structure will be for
|
|
// using the NT logging functions, I will enumerate it here to hopefully avoid
|
|
// confusion and/or refresh my own memory and a later time.
|
|
//
|
|
// TEST -- test api function -- for it to pass there must be no failures during
|
|
// the test, beginning of api should have START_TEST()
|
|
//
|
|
// TEST ITERATION -- one iteration of the full test, one of API
|
|
// user parameters is the # of iterations to perform
|
|
// Beginning of each iteration loop should have
|
|
// START_TEST_ITERATION() call
|
|
//
|
|
// VARIATION -- test is made up of a series of variations, a
|
|
// variation is considered to be one check
|
|
// of the validity of a test. This usually
|
|
// tweaking one major parameter to the tested
|
|
// function (ie. a different file object, or
|
|
// invalid Preparsed data) These are the
|
|
// most common variations that will be used
|
|
//
|
|
// INT_VAR_RESULT -- Abbreviated for INTERMEDIATE_VARIATION_RESULT
|
|
// This is some sort of information that gets
|
|
// logged about the current state of the variation
|
|
// being performed. It uses the NT logging
|
|
// type of TL_INFO. It's mainly used to generate
|
|
// a little more information for a debugging case
|
|
//
|
|
// VARIATION_RESULT -- Actual result of a variation -- This is where
|
|
// the variation is logged as either pass or
|
|
// fail. A variation may fail due to a reason
|
|
// that is specified by INT_VAR_RESULT but
|
|
// the actual error code is set here.
|
|
//
|
|
// END_VARIATION -- Macro called to indicate the end of a variation
|
|
//
|
|
// END_TEST_ITERATION -- Macro called to signify the end of one particular
|
|
// iteration of the test -- Another iteration should
|
|
// perform the exact same variations with the
|
|
// exact same conditions.
|
|
//
|
|
// END_TEST -- Macro called to indicate the end of the test
|
|
// API -- No other information should be logged
|
|
// at this point
|
|
//
|
|
// There are two other macros that do some sort of logging and have some sort
|
|
// of meaning to the test suite. However, they do not relate to the general
|
|
// test structure above although they may effect the operation of a test.
|
|
//
|
|
// WARNING -- Warning is used to indicate that something was
|
|
// found that may be incorrect but is not something
|
|
// that is the focus of the test API and therefore
|
|
// is not flagged as a variation failure. An example
|
|
// of a warning is a HID API returning the wrong
|
|
// error code. Since the HID API that returned the
|
|
// code was not the one that was being directly tested
|
|
// but being used to validate the APIs currently
|
|
// under test.
|
|
//
|
|
// TEST_ERROR -- Test error is something beyond our control which
|
|
// will not let the test continue but is not due
|
|
// to a variation failure. An example of a test
|
|
// error is the failure of a memory allocation
|
|
//
|
|
// Hopefully, that will provide a little more info and add a tad more understanding
|
|
// to the implementation below. Hopefully, I'll be able to go back to it
|
|
// and keep me from getting confused.
|
|
*/
|
|
|
|
/*****************************************************************************
|
|
/* HidTest include files
|
|
/*****************************************************************************/
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include <wtypes.h>
|
|
#include <limits.h>
|
|
#include "hidsdi.h"
|
|
|
|
#define USE_MACROS
|
|
#include "list.h"
|
|
|
|
#include "hidtest.h"
|
|
#include "log.h"
|
|
#include "handle.h"
|
|
#include "buffer.h"
|
|
#include "debug.h"
|
|
|
|
/*
|
|
// Include hidclass.h so we can get the HID_CLASS GUID value
|
|
*/
|
|
|
|
#define INITGUID
|
|
#include "hidclass.h"
|
|
|
|
/*****************************************************************************
|
|
/* Local macro definitions
|
|
/*****************************************************************************/
|
|
#define HIDTest_CompareAttributes(pa1, pa2) (((pa1) -> Size == (pa2) -> Size) && \
|
|
((pa1) -> VendorID == (pa2) -> VendorID) && \
|
|
((pa1) -> ProductID == (pa2) -> ProductID) && \
|
|
((pa1) -> VersionNumber == (pa2) -> VersionNumber))
|
|
|
|
|
|
#define HIDTest_CompareStrings(s1, s1len, s2, s2len) (((s1len) == (s2len)) && \
|
|
(0 == memcmp((s1), (s2), (s1len))))
|
|
|
|
|
|
/*****************************************************************************
|
|
/* Miscellaneous other definitions
|
|
/*****************************************************************************/
|
|
|
|
#define STRINGS_TO_TEST 0x100
|
|
#define INIT_STR_LIST_SIZE 4
|
|
#define STRING_INDEX_MANUFACTURER 0xFFFFFFFC
|
|
#define STRING_INDEX_PRODUCT 0xFFFFFFFD
|
|
#define STRING_INDEX_SERIAL_NUMBER 0xFFFFFFFE
|
|
|
|
#define COMPARE_GUIDS(guid1, guid2) ((0 == memcmp(&(guid1), &(guid2), sizeof(GUID))))
|
|
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
|
|
|
#define CURRENT_BUFFER_MIN 2
|
|
#define RANDOM_BUFFER_VALUE 10
|
|
|
|
/*****************************************************************************
|
|
/* Module specific typedefs
|
|
/*****************************************************************************/
|
|
typedef struct _DataIndexList {
|
|
|
|
ULONG MaxDataIndex;
|
|
BOOL *List;
|
|
|
|
} DATA_INDEX_LIST, *PDATA_INDEX_LIST;
|
|
|
|
|
|
/*****************************************************************************
|
|
/* DLL exportable variables with exportable function address
|
|
/*****************************************************************************/
|
|
|
|
PHIDTEST_API HIDTest_VerifyHidGuid;
|
|
PHIDTEST_API HIDTest_VerifyStrings;
|
|
PHIDTEST_API HIDTest_VerifyPreparsedData;
|
|
PHIDTEST_API HIDTest_VerifyAttributes;
|
|
PHIDTEST_API HIDTest_VerifyCapabilities;
|
|
|
|
PCREATE_PHYSICAL_DEVICE_INFO_PROC HIDTest_CreatePhysicalDeviceInfo;
|
|
PCREATE_LOGICAL_DEVICE_INFO_PROC HIDTest_CreateLogicalDeviceInfo;
|
|
PCREATE_FREE_DEVICE_INFO_PROC HIDTest_FreeDeviceInfo;
|
|
PCREATE_TEST_LOG_PROC HIDTest_CreateTestLog;
|
|
PSET_LOG_ON_PROC HIDTest_SetLogOn;
|
|
PCLOSE_TEST_LOG_PROC HIDTest_CloseTestLog;
|
|
|
|
/*****************************************************************************
|
|
/* Local function declarations
|
|
/*****************************************************************************/
|
|
|
|
BOOL
|
|
HIDTest_FillCommonDeviceInfo(
|
|
OUT PHIDTEST_DEVICEINFO DeviceInfo
|
|
);
|
|
|
|
|
|
BOOL
|
|
HIDTest_FillValueCaps(
|
|
IN HIDP_REPORT_TYPE ReportType,
|
|
IN PHIDP_VALUE_CAPS *CapsBuffer,
|
|
IN ULONG NumCaps,
|
|
IN PHIDP_PREPARSED_DATA Ppd
|
|
);
|
|
|
|
|
|
BOOL
|
|
HIDTest_FillButtonCaps(
|
|
IN HIDP_REPORT_TYPE ReportType,
|
|
IN PHIDP_BUTTON_CAPS *CapsBuffer,
|
|
IN ULONG NumCaps,
|
|
IN PHIDP_PREPARSED_DATA Ppd
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_DoGetFreePpd(
|
|
IN HANDLE HidDevice
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_ValidateAttributes(
|
|
IN HANDLE HidDevice,
|
|
IN PHIDD_ATTRIBUTES Attrib
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_ValidateCaps(
|
|
IN PHIDP_PREPARSED_DATA HidPpd,
|
|
IN PHIDP_CAPS HidCaps
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_ValidateBufferValues(
|
|
IN HIDP_REPORT_TYPE ReportType,
|
|
IN USHORT NumButtonCaps,
|
|
IN USHORT NumValueCaps,
|
|
IN USHORT NumDataIndices,
|
|
IN PHIDP_PREPARSED_DATA Ppd
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_ValidateNumInputBuffers(
|
|
IN HANDLE HandleToTest,
|
|
IN HANDLE SecondHandle,
|
|
IN BOOL IsFirstHandleLegal,
|
|
IN PULONG TestBuffer
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_GetBufferCount(
|
|
IN HANDLE DeviceHandle,
|
|
IN PULONG TestBuffer
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_ValidateStrings(
|
|
HANDLE DeviceHandle,
|
|
PLIST MasterList,
|
|
IN ULONG ManufacturerStringIndex,
|
|
IN ULONG ProductStringIndex,
|
|
IN ULONG SerialNumberStringIndex
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_ValidateStdStrings(
|
|
IN HANDLE DeviceHandle,
|
|
IN PLIST CurrentStringList,
|
|
IN ULONG ManufacturerStringIndex,
|
|
IN ULONG ProductStringIndex,
|
|
IN ULONG SerialNumberStringIndex
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_ValidateStringParams(
|
|
IN HANDLE HidDevice,
|
|
IN PLIST StringList,
|
|
IN ULONG ManufacturerStringIndex,
|
|
IN ULONG ProductStringIndex,
|
|
IN ULONG SerialNumberStringIndex
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_ValidateStringIndexParams(
|
|
IN HANDLE HidDevice,
|
|
IN ULONG StringIndex,
|
|
IN ULONG StringLength,
|
|
IN PCHAR StringTypeDesc
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_CompareCaps(
|
|
IN PHIDP_CAPS Caps1,
|
|
IN PHIDP_CAPS Caps2
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_InitDataIndexList(
|
|
IN ULONG MaxDataIndex,
|
|
OUT PDATA_INDEX_LIST IndexList
|
|
);
|
|
|
|
VOID
|
|
HIDTest_FreeDataIndexList(
|
|
IN PDATA_INDEX_LIST IndexList
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_MarkDataIndex(
|
|
IN PDATA_INDEX_LIST IndexList,
|
|
IN ULONG IndexValue
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_GetDataIndexStatus(
|
|
IN PDATA_INDEX_LIST IndexList,
|
|
IN ULONG IndexValue
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_AreAllIndicesUsed(
|
|
IN PDATA_INDEX_LIST IndexList
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_DoesStringExist(
|
|
HANDLE DeviceHandle,
|
|
ULONG StringIndex,
|
|
PWCHAR *String,
|
|
PULONG StringLength,
|
|
BOOL *StringExists
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_GetWideStringLength(
|
|
PWCHAR String,
|
|
ULONG StringSize,
|
|
PULONG StringLength
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_GetString(
|
|
HANDLE DeviceHandle,
|
|
ULONG StringIndex,
|
|
PWCHAR StringBuffer,
|
|
ULONG BufferLength,
|
|
PULONG ErrorCode
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_BuildStringList(
|
|
HANDLE DeviceHandle,
|
|
PLIST StringList
|
|
);
|
|
|
|
VOID
|
|
HIDTest_FreeStringList(
|
|
PLIST StringList
|
|
);
|
|
|
|
VOID
|
|
HIDTest_FreeStringNodeCallback(
|
|
PLIST_NODE_HDR ListNode
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_AddStringToStringList(
|
|
PLIST StringList,
|
|
ULONG StringIndex,
|
|
PWCHAR String,
|
|
ULONG StringLength
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_IsStrIndexInStrList(
|
|
PLIST StringList,
|
|
ULONG StringIndex,
|
|
PSTRING_LIST_NODE StringNode
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_IsStringInStrList(
|
|
PLIST StringList,
|
|
PWCHAR String,
|
|
ULONG StringLength,
|
|
PSTRING_LIST_NODE StringNode
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_CompareStringLists(
|
|
PLIST StringList1,
|
|
PLIST StringList2
|
|
);
|
|
|
|
VOID
|
|
HIDTest_CallGetFeature(
|
|
IN HANDLE DeviceHandle,
|
|
IN UCHAR ReportID,
|
|
IN PUCHAR ReportBuffer,
|
|
IN ULONG ReportBufferLength,
|
|
OUT PBOOL ReturnStatus,
|
|
OUT PULONG ErrorCode,
|
|
OUT PBOOL BufferStatus
|
|
);
|
|
|
|
VOID
|
|
HIDTest_CallSetFeature(
|
|
IN HANDLE DeviceHandle,
|
|
IN UCHAR ReportID,
|
|
IN PUCHAR ReportBuffer,
|
|
IN ULONG ReportBufferLength,
|
|
OUT PBOOL ReturnStatus,
|
|
OUT PULONG ErrorCode,
|
|
OUT PBOOL BufferStatus
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_BuildReportIDList(
|
|
IN PHIDP_VALUE_CAPS VCaps,
|
|
IN ULONG NumVCaps,
|
|
IN PHIDP_BUTTON_CAPS BCaps,
|
|
IN ULONG NumBCaps,
|
|
IN PUCHAR *ReportIDs,
|
|
IN PULONG ReportIDCount
|
|
);
|
|
|
|
VOID
|
|
HIDTest_InsertIDIntoList(
|
|
IN PUCHAR ReportIDs,
|
|
IN PULONG ReportIDCount,
|
|
IN UCHAR NewID
|
|
);
|
|
|
|
BOOL
|
|
HIDTest_IsIDInList(
|
|
IN UCHAR ReportID,
|
|
IN PUCHAR ReportIDList,
|
|
IN ULONG ReportIDListCount
|
|
);
|
|
|
|
/*****************************************************************************
|
|
/* Exportable function definitions
|
|
/*****************************************************************************/
|
|
|
|
BOOLEAN __stdcall
|
|
DllMain(
|
|
HINSTANCE hinst,
|
|
DWORD dwReason,
|
|
LPVOID lpReserved
|
|
)
|
|
{
|
|
switch (dwReason) {
|
|
default: return TRUE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
HIDTest_InitExportAddress(
|
|
PHIDTEST_FUNCTIONS Exports
|
|
)
|
|
{
|
|
Exports -> HIDTest_VerifyHidGuid = &HIDTest_VerifyHidGuidA;
|
|
Exports -> HIDTest_VerifyStrings = &HIDTest_VerifyStringsA;
|
|
Exports -> HIDTest_VerifyPreparsedData = &HIDTest_VerifyPreparsedDataA;
|
|
Exports -> HIDTest_VerifyAttributes = &HIDTest_VerifyAttributesA;
|
|
Exports -> HIDTest_VerifyNumBuffers = &HIDTest_VerifyNumBuffersA;
|
|
Exports -> HIDTest_VerifyCapabilities = &HIDTest_VerifyCapabilitiesA;
|
|
Exports -> HIDTest_VerifyGetFeature = &HIDTest_VerifyGetFeatureA;
|
|
Exports -> HIDTest_VerifySetFeature = &HIDTest_VerifySetFeatureA;
|
|
|
|
Exports -> HIDTest_CreatePhysicalDeviceInfo = &HIDTest_CreatePhysicalDeviceInfoA;
|
|
Exports -> HIDTest_CreateLogicalDeviceInfo = &HIDTest_CreateLogicalDeviceInfoA;
|
|
Exports -> HIDTest_FreeDeviceInfo = &HIDTest_FreeDeviceInfoA;
|
|
Exports -> HIDTest_CreateTestLog = &HIDTest_CreateTestLogA;
|
|
Exports -> HIDTest_SetLogOn = &HIDTest_SetLogOnA;
|
|
Exports -> HIDTest_CloseTestLog = &HIDTest_CloseTestLogA;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
HIDTest_VerifyHidGuidA(
|
|
IN PHIDTEST_DEVICEINFO HidDevice,
|
|
IN ULONG nIterations,
|
|
OUT PHIDTEST_STATUS Status
|
|
)
|
|
{
|
|
ULONG OperationCount;
|
|
ULONG IterationCount;
|
|
ULONG FailedCount;
|
|
GUID *CallGuid;
|
|
|
|
/*
|
|
// Testing the HidD_GetHidGuid function is relatively simple. It consists of
|
|
// one variation which calls HidD_GetHidGuid nIterations times and compares
|
|
// the received guid with the standard hidclass guid.
|
|
*/
|
|
|
|
OperationCount = 0;
|
|
FailedCount = 0;
|
|
|
|
START_TEST("HIDTest_HidD_GetHidGuid");
|
|
|
|
CallGuid = (GUID *) AllocateTestBuffer(sizeof(GUID));
|
|
|
|
if (NULL == CallGuid) {
|
|
|
|
LOG_TEST_ERROR("Couldn't allocate memory");
|
|
return;
|
|
|
|
}
|
|
|
|
for (IterationCount = 1; IterationCount <= nIterations; IterationCount++) {
|
|
|
|
START_TEST_ITERATION(IterationCount);
|
|
|
|
START_VARIATION("Validating the HID Class GUID");
|
|
|
|
HidD_GetHidGuid(CallGuid);
|
|
|
|
if (!ValidateTestBuffer(CallGuid)) {
|
|
|
|
LOG_VARIATION_RESULT(TLS_SEV3, "Buffer violation");
|
|
FailedCount++;
|
|
|
|
}
|
|
else if (!COMPARE_GUIDS(GUID_CLASS_INPUT, *CallGuid)) {
|
|
LOG_VARIATION_FAIL();
|
|
FailedCount++;
|
|
}
|
|
else {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
OperationCount++;
|
|
|
|
END_VARIATION();
|
|
|
|
END_TEST_ITERATION();
|
|
}
|
|
|
|
END_TEST();
|
|
|
|
FreeTestBuffer(CallGuid);
|
|
|
|
Status -> nOperationsPerformed = OperationCount;
|
|
Status -> nOperationsFailed = FailedCount;
|
|
Status -> nOperationsPassed = OperationCount - FailedCount;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
HIDTest_VerifyPreparsedDataA(
|
|
IN PHIDTEST_DEVICEINFO HidDevice,
|
|
IN ULONG nIterations,
|
|
OUT PHIDTEST_STATUS Status
|
|
)
|
|
{
|
|
ULONG OperationCount;
|
|
ULONG IterationCount;
|
|
ULONG FailedCount;
|
|
BOOL CallStatus;
|
|
HANDLE CurrDeviceHandle;
|
|
BOOL IsLegalDeviceHandle;
|
|
static HANDLE HandleList[MAX_NUM_HANDLES];
|
|
|
|
/*
|
|
// This test is made up of a number of different variations depending on
|
|
// the number of handles that will be used but the basic concept is the
|
|
// following.
|
|
//
|
|
// 1) For each handle that we get from the handle generator
|
|
// attempt to get preparsed data for that handle and compare
|
|
// the results of the Get/Free status with what we should expect
|
|
// to receive depending on the validity of the handle. The results
|
|
// we expect to see is what we have stored for PreparsedData in the
|
|
// HidDevice struct.
|
|
//
|
|
// 2) Also, if during the above iterations we encounter a legal handle
|
|
// value, we will try to GetPreparsed data with a NULL value
|
|
// passed in as the second paramters. The call should simply
|
|
// fail.
|
|
//
|
|
// 3) After all is said and done, we attempt to free bogus buffers,
|
|
// both NULL and the HandleList buffer. See comments below for
|
|
// the reason, HandleList is to be freed.
|
|
//
|
|
// The above steps are all variations of the test, and the variations
|
|
// must pass for nIterations in order for the variation to pass.
|
|
// Each separate handle is also a variation. Basically,
|
|
// there will be 2N + 1 variations where N is the number
|
|
// of handles being tested.
|
|
*/
|
|
|
|
START_TEST("HIDTest_VerifyPreparsedData");
|
|
|
|
/*
|
|
// Initialize the test statistics variables
|
|
*/
|
|
|
|
FailedCount = 0;
|
|
OperationCount = 0;
|
|
|
|
/*
|
|
// This test is made up of three variations, each of which is performs
|
|
// the same operation nIterations number of times. To pass a variation
|
|
// all iterations must pass.
|
|
*/
|
|
|
|
|
|
/*
|
|
// Begin by initializing the device handles that will be used for the test
|
|
// A device handle list is built and passed into InitDeviceHandles.
|
|
// Subsequent calls to GetDeviceHandle will return either one of the
|
|
// handles that is specified in this list or another handle that the
|
|
// "Handle Manager" has decided should be tested as well.
|
|
*/
|
|
|
|
if (IS_VALID_DEVICE_HANDLE(HidDevice -> HidDeviceHandle)) {
|
|
HandleList[0] = HidDevice -> HidDeviceHandle;
|
|
|
|
ASSERT (NULL != HidDevice -> HidDeviceString);
|
|
|
|
HIDTest_InitDeviceHandles(HidDevice -> HidDeviceString,
|
|
2,
|
|
1,
|
|
HandleList
|
|
);
|
|
|
|
}
|
|
else {
|
|
|
|
LOG_TEST_ERROR("HidDevice Handle is invalid -- cannot perform test");
|
|
goto VERIFY_PPD_END;
|
|
|
|
}
|
|
|
|
/*
|
|
// The testing of the preparsed data function involves attempting to get
|
|
// and set the free preparsed data nIterations number of times for a
|
|
// given device handle. If the device handle is not a legal one,
|
|
// then the CallStatus should be FALSE instead of TRUE
|
|
//
|
|
*/
|
|
|
|
/*
|
|
// Start the iteration loop
|
|
*/
|
|
|
|
for (IterationCount = 1; IterationCount <= nIterations; IterationCount++) {
|
|
|
|
START_TEST_ITERATION( IterationCount );
|
|
|
|
HIDTest_ResetDeviceHandles();
|
|
|
|
while (HIDTest_GetDeviceHandle(&CurrDeviceHandle, &IsLegalDeviceHandle)) {
|
|
|
|
START_VARIATION_ON_DEVICE_HANDLE(CurrDeviceHandle,
|
|
IsLegalDeviceHandle
|
|
);
|
|
|
|
/*
|
|
// If the handle that is being tested is a valid one, then the
|
|
// below call should return TRUE and FALSE otherwise. Therefore
|
|
// we need only compare the return value with the IsLegalDeviceHandle
|
|
// variable to determine if the call really failed or not.
|
|
*/
|
|
|
|
CallStatus = HIDTest_DoGetFreePpd(CurrDeviceHandle);
|
|
|
|
if (CallStatus != IsLegalDeviceHandle) {
|
|
|
|
LOG_VARIATION_FAIL();
|
|
FailedCount++;
|
|
|
|
}
|
|
else {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
|
|
OperationCount++;
|
|
|
|
END_VARIATION();
|
|
}
|
|
|
|
/*
|
|
// Start another variation, this time, we attempt to free a block of
|
|
// PREPARSED_DATA which is not actually a block of preparsed data
|
|
*/
|
|
|
|
START_VARIATION("Attempting to free non-preparsed data block\n");
|
|
|
|
/*
|
|
// We'll pass in the address of the HandleList for three reasons
|
|
// 1) If the function touches any part of this memory, it had better
|
|
// be a valid address so we don't page fault
|
|
// 2) If the function touches the memory, it should be big enough
|
|
// for the same reason -- there's a good chance this will be
|
|
// big enough.
|
|
// 3) If he garbles any of the data, it's not going to kill our app
|
|
// The list is defined static so that the procedure doesn't try
|
|
// to free stack space either. Basically, if it does try to free
|
|
// this memory block, we at least should be able to determine
|
|
// that he freed the block instead of possibly having the OS go off
|
|
// into la-la land and not knowing what happened.
|
|
*/
|
|
|
|
CallStatus = HidD_FreePreparsedData((PHIDP_PREPARSED_DATA) (&HandleList));
|
|
|
|
if (CallStatus) {
|
|
FailedCount++;
|
|
LOG_VARIATION_FAIL();
|
|
}
|
|
else {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
OperationCount++;
|
|
|
|
END_VARIATION();
|
|
|
|
END_TEST_ITERATION();
|
|
|
|
}
|
|
HIDTest_CloseDeviceHandles();
|
|
|
|
VERIFY_PPD_END:
|
|
|
|
END_TEST();
|
|
|
|
Status -> nOperationsPerformed = OperationCount;
|
|
Status -> nOperationsPassed = OperationCount - FailedCount;
|
|
Status -> nOperationsFailed = FailedCount;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
HIDTest_VerifyAttributesA(
|
|
IN PHIDTEST_DEVICEINFO HidDevice,
|
|
IN ULONG nIterations,
|
|
OUT PHIDTEST_STATUS Status
|
|
)
|
|
{
|
|
ULONG OperationCount;
|
|
ULONG IterationCount;
|
|
ULONG FailedCount;
|
|
BOOL CallStatus;
|
|
HANDLE HandleList[MAX_NUM_HANDLES];
|
|
HANDLE CurrDeviceHandle;
|
|
BOOL IsLegalDeviceHandle;
|
|
|
|
FailedCount = 0;
|
|
OperationCount = 0;
|
|
|
|
START_TEST("HIDTest_VerifyAttributes");
|
|
|
|
/*
|
|
// Setup our device handle list but only if our initial device
|
|
// handle is legitimate and we have HidDeviceAttributes. This should
|
|
// weed out those danged logical devices that I'm currently hallucinating
|
|
// we'll be implemented.
|
|
*/
|
|
|
|
if ((IS_VALID_DEVICE_HANDLE(HidDevice -> HidDeviceHandle)) &&
|
|
(NULL != HidDevice -> HidDeviceAttributes)) {
|
|
|
|
HandleList[0] = HidDevice -> HidDeviceHandle;
|
|
|
|
ASSERT (NULL != HidDevice -> HidDeviceString);
|
|
|
|
HIDTest_InitDeviceHandles(HidDevice -> HidDeviceString,
|
|
2,
|
|
1,
|
|
HandleList
|
|
);
|
|
|
|
}
|
|
else {
|
|
|
|
LOG_TEST_ERROR("HidDevice Handle is invalid -- cannot perform test");
|
|
goto VERIFY_ATTRIB_END;
|
|
|
|
}
|
|
|
|
/*
|
|
// This test involves a variation for each of the different device handles
|
|
// that is generated by our device handle generator. We will not test
|
|
// with a NULL pointer to the attributes structure since the expected
|
|
// behavior with such a pointer is to terminate the app.
|
|
*/
|
|
|
|
for (IterationCount = 1; IterationCount <= nIterations; IterationCount++) {
|
|
|
|
START_TEST_ITERATION( IterationCount );
|
|
|
|
HIDTest_ResetDeviceHandles();
|
|
|
|
while ( HIDTest_GetDeviceHandle( &CurrDeviceHandle, &IsLegalDeviceHandle )) {
|
|
|
|
START_VARIATION_ON_DEVICE_HANDLE( CurrDeviceHandle,
|
|
IsLegalDeviceHandle
|
|
);
|
|
|
|
CallStatus = HIDTest_ValidateAttributes( CurrDeviceHandle,
|
|
HidDevice -> HidDeviceAttributes
|
|
);
|
|
|
|
if (CallStatus != IsLegalDeviceHandle) {
|
|
|
|
LOG_VARIATION_FAIL();
|
|
FailedCount++;
|
|
|
|
}
|
|
else {
|
|
|
|
LOG_VARIATION_PASS();
|
|
|
|
}
|
|
OperationCount++;
|
|
|
|
END_VARIATION();
|
|
|
|
}
|
|
|
|
END_TEST_ITERATION();
|
|
|
|
}
|
|
|
|
HIDTest_CloseDeviceHandles();
|
|
|
|
VERIFY_ATTRIB_END:
|
|
|
|
END_TEST();
|
|
|
|
Status -> nOperationsPerformed = OperationCount;
|
|
Status -> nOperationsPassed = OperationCount - FailedCount;
|
|
Status -> nOperationsFailed = FailedCount;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
HIDTest_VerifyCapabilitiesA(
|
|
IN PHIDTEST_DEVICEINFO HidDevice,
|
|
IN ULONG nIterations,
|
|
OUT PHIDTEST_STATUS Status
|
|
)
|
|
{
|
|
HIDP_CAPS HidCaps;
|
|
NTSTATUS CapsStatus;
|
|
ULONG IterationCount;
|
|
ULONG OperationCount;
|
|
ULONG FailedCount;
|
|
BOOL CallStatus;
|
|
ULONG BadPpd[16];
|
|
|
|
|
|
/*
|
|
// This is a more straight forward piece of code. We're not going to
|
|
// build a device list because we're not going to use the device handles.
|
|
// In fact, we're just going to use the Ppd data structure to verify
|
|
// everything. However, we will pass at least one bad Ppd structure
|
|
// to verify that the signature checking works.
|
|
*/
|
|
|
|
/*
|
|
// What we need to do is call the verify routine with the preparsed data
|
|
// for nIterations to verify that this stuff works all the time. Therefore
|
|
// there exist only two varitions for this test. One with good preparsed
|
|
// data on nIterations and another with bogus Ppd for nIterations
|
|
*/
|
|
|
|
START_TEST("HIDTest_VerifyCapabilities");
|
|
|
|
OperationCount = 0;
|
|
FailedCount = 0;
|
|
|
|
for (IterationCount = 0; IterationCount < nIterations; IterationCount++) {
|
|
|
|
START_TEST_ITERATION(IterationCount);
|
|
|
|
START_VARIATION("Validating Capabilities -- Good Ppd");
|
|
|
|
CallStatus = HIDTest_ValidateCaps(HidDevice -> HidDevicePpd,
|
|
HidDevice -> HidDeviceCaps
|
|
);
|
|
if (!CallStatus) {
|
|
LOG_VARIATION_FAIL();
|
|
FailedCount++;
|
|
}
|
|
else {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
OperationCount++;
|
|
|
|
END_VARIATION();
|
|
|
|
|
|
/*
|
|
// Let's do the same thing, only this time with bogus Ppd. To insure
|
|
// that the correct error code is returned, we won't call
|
|
// ValidateCaps but instead just call Hidp_GetCaps from here
|
|
// to insure INVALID_PREPARSED_DATA is returned.
|
|
*/
|
|
|
|
START_VARIATION("Validating Capabilities -- Bad Ppd");
|
|
|
|
CapsStatus = HidP_GetCaps((PHIDP_PREPARSED_DATA) &BadPpd[0],
|
|
&HidCaps
|
|
);
|
|
|
|
if (HIDP_STATUS_INVALID_PREPARSED_DATA != CapsStatus) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Unexpected error code returned");
|
|
LOG_VARIATION_FAIL();
|
|
FailedCount++;
|
|
|
|
}
|
|
else {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
OperationCount++;
|
|
|
|
|
|
END_VARIATION();
|
|
|
|
END_TEST_ITERATION();
|
|
|
|
}
|
|
|
|
END_TEST();
|
|
|
|
Status -> nOperationsPerformed = OperationCount;
|
|
Status -> nOperationsPassed = OperationCount - FailedCount;
|
|
Status -> nOperationsFailed = FailedCount;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
HIDTest_VerifyStringsA(
|
|
IN PHIDTEST_DEVICEINFO HidDevice,
|
|
IN ULONG nIterations,
|
|
OUT PHIDTEST_STATUS Status
|
|
)
|
|
{
|
|
ULONG IterationCount;
|
|
ULONG OperationCount;
|
|
ULONG FailedCount;
|
|
HANDLE HandleList[MAX_NUM_HANDLES];
|
|
HANDLE CurrDeviceHandle;
|
|
BOOL IsLegalDeviceHandle;
|
|
BOOL CallStatus;
|
|
|
|
|
|
START_TEST("HIDTest_VerifyStrings");
|
|
|
|
OperationCount = 0;
|
|
FailedCount = 0;
|
|
|
|
/*
|
|
// Attempt to setup the device handle list, if the current
|
|
// HidDeviceHandle is not valid than we cannot proceed any farther
|
|
*/
|
|
|
|
if (IS_VALID_DEVICE_HANDLE(HidDevice -> HidDeviceHandle)) {
|
|
HandleList[0] = HidDevice -> HidDeviceHandle;
|
|
|
|
ASSERT (NULL != HidDevice -> HidDeviceString);
|
|
|
|
HIDTest_InitDeviceHandles(HidDevice -> HidDeviceString,
|
|
2,
|
|
1,
|
|
HandleList
|
|
);
|
|
|
|
}
|
|
else {
|
|
|
|
LOG_TEST_ERROR("HidDevice Handle is invalid -- cannot perform test");
|
|
goto VERIFY_STRINGS_END;
|
|
|
|
}
|
|
|
|
/*
|
|
// Start the iterations of the string test
|
|
*/
|
|
|
|
for (IterationCount = 1; IterationCount <= nIterations; IterationCount++) {
|
|
|
|
START_TEST_ITERATION(IterationCount);
|
|
|
|
HIDTest_ResetDeviceHandles();
|
|
|
|
while ( HIDTest_GetDeviceHandle( &CurrDeviceHandle, &IsLegalDeviceHandle )) {
|
|
|
|
START_VARIATION_ON_DEVICE_HANDLE( CurrDeviceHandle,
|
|
IsLegalDeviceHandle
|
|
);
|
|
|
|
CallStatus = HIDTest_ValidateStrings( CurrDeviceHandle,
|
|
&HidDevice -> StringList,
|
|
HidDevice -> ManufacturerStringIndex,
|
|
HidDevice -> ProductStringIndex,
|
|
HidDevice -> SerialNumberStringIndex
|
|
);
|
|
|
|
if (CallStatus != IsLegalDeviceHandle) {
|
|
|
|
LOG_VARIATION_FAIL();
|
|
FailedCount++;
|
|
}
|
|
else {
|
|
|
|
LOG_VARIATION_PASS();
|
|
|
|
}
|
|
OperationCount++;
|
|
|
|
END_VARIATION();
|
|
}
|
|
|
|
START_VARIATION("Testing the HidD_ string parameter validation");
|
|
|
|
CallStatus = HIDTest_ValidateStringParams( HidDevice -> HidDeviceHandle,
|
|
&HidDevice -> StringList,
|
|
HidDevice -> ManufacturerStringIndex,
|
|
HidDevice -> ProductStringIndex,
|
|
HidDevice -> SerialNumberStringIndex
|
|
);
|
|
|
|
if (!CallStatus) {
|
|
|
|
LOG_VARIATION_FAIL();
|
|
FailedCount++;
|
|
|
|
}
|
|
else {
|
|
|
|
LOG_VARIATION_PASS();
|
|
|
|
}
|
|
OperationCount++;
|
|
|
|
END_TEST_ITERATION();
|
|
}
|
|
|
|
HIDTest_CloseDeviceHandles();
|
|
|
|
VERIFY_STRINGS_END:
|
|
|
|
END_TEST();
|
|
|
|
Status -> nOperationsPerformed = OperationCount;
|
|
Status -> nOperationsPassed = OperationCount - FailedCount;
|
|
Status -> nOperationsFailed = FailedCount;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
HIDTest_VerifyNumBuffersA(
|
|
IN PHIDTEST_DEVICEINFO HidDevice,
|
|
IN ULONG nIterations,
|
|
OUT PHIDTEST_STATUS Status
|
|
)
|
|
{
|
|
ULONG iterationCount;
|
|
ULONG operationCount;
|
|
ULONG failedCount;
|
|
PULONG bufCount;
|
|
HANDLE secondHandle;
|
|
HANDLE handleList[MAX_NUM_HANDLES];
|
|
HANDLE currDeviceHandle;
|
|
BOOL isLegalDeviceHandle;
|
|
BOOL callStatus;
|
|
|
|
START_TEST("HIDTest_VerifyNumInputBuffers");
|
|
|
|
operationCount = 0;
|
|
failedCount = 0;
|
|
bufCount = NULL;
|
|
|
|
secondHandle = CreateFile( HidDevice -> HidDeviceString,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (INVALID_HANDLE_VALUE == secondHandle) {
|
|
|
|
LOG_TEST_ERROR("Couldn't open second handle to HID device");
|
|
goto VERIFY_NUMBUFFERS_END;
|
|
|
|
}
|
|
|
|
bufCount = (PULONG) AllocateTestBuffer(sizeof(ULONG));
|
|
|
|
if (NULL == bufCount) {
|
|
|
|
LOG_TEST_ERROR( "Couldn't allocate memory" );
|
|
goto VERIFY_NUMBUFFERS_END;
|
|
|
|
}
|
|
|
|
/*
|
|
// Attempt to setup the device handle list, if the current
|
|
// HidDeviceHandle is not valid than we cannot proceed any farther
|
|
*/
|
|
|
|
if (IS_VALID_DEVICE_HANDLE(HidDevice -> HidDeviceHandle)) {
|
|
handleList[0] = HidDevice -> HidDeviceHandle;
|
|
|
|
ASSERT (NULL != HidDevice -> HidDeviceString);
|
|
|
|
HIDTest_InitDeviceHandles(HidDevice -> HidDeviceString,
|
|
2,
|
|
1,
|
|
handleList
|
|
);
|
|
|
|
}
|
|
else {
|
|
|
|
LOG_TEST_ERROR("HidDevice Handle is invalid -- cannot perform test");
|
|
goto VERIFY_NUMBUFFERS_END;
|
|
|
|
}
|
|
|
|
for (iterationCount = 1; iterationCount <= nIterations; iterationCount++) {
|
|
|
|
START_TEST_ITERATION(iterationCount);
|
|
|
|
HIDTest_ResetDeviceHandles();
|
|
|
|
while ( HIDTest_GetDeviceHandle( &currDeviceHandle, &isLegalDeviceHandle )) {
|
|
|
|
START_VARIATION_ON_DEVICE_HANDLE( currDeviceHandle,
|
|
isLegalDeviceHandle
|
|
);
|
|
|
|
callStatus = HIDTest_ValidateNumInputBuffers( currDeviceHandle,
|
|
secondHandle,
|
|
isLegalDeviceHandle,
|
|
bufCount
|
|
);
|
|
|
|
if (!callStatus) {
|
|
|
|
LOG_VARIATION_FAIL();
|
|
failedCount++;
|
|
}
|
|
else {
|
|
|
|
LOG_VARIATION_PASS();
|
|
|
|
}
|
|
operationCount++;
|
|
|
|
END_VARIATION();
|
|
}
|
|
}
|
|
|
|
VERIFY_NUMBUFFERS_END:
|
|
|
|
END_TEST();
|
|
|
|
if (NULL != bufCount) {
|
|
FreeTestBuffer(bufCount);
|
|
}
|
|
|
|
if (INVALID_HANDLE_VALUE != secondHandle) {
|
|
CloseHandle(secondHandle);
|
|
}
|
|
|
|
Status -> nOperationsPerformed = operationCount;
|
|
Status -> nOperationsPassed = operationCount - failedCount;
|
|
Status -> nOperationsFailed = failedCount;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
HIDTest_VerifyGetFeatureA(
|
|
IN PHIDTEST_DEVICEINFO HidDevice,
|
|
IN ULONG nIterations,
|
|
OUT PHIDTEST_STATUS Status
|
|
)
|
|
{
|
|
ULONG iterationCount;
|
|
ULONG operationCount;
|
|
ULONG failedCount;
|
|
PCHAR featureBuffer;
|
|
ULONG featureBufferLength;
|
|
HANDLE handleList[MAX_NUM_HANDLES];
|
|
HANDLE currDeviceHandle;
|
|
BOOL isLegalDeviceHandle;
|
|
BOOL returnStatus;
|
|
BOOL bufferStatus;
|
|
ULONG errorCode;
|
|
PUCHAR reportIDList;
|
|
ULONG reportIDListCount;
|
|
ULONG idIndex;
|
|
UCHAR reportID;
|
|
BOOL variationStatus;
|
|
ULONG reportBufferSize;
|
|
static CHAR varString[128];
|
|
|
|
START_TEST("HIDTest_VerifyGetFeature");
|
|
|
|
operationCount = 0;
|
|
failedCount = 0;
|
|
|
|
/*
|
|
// If we have no features on the device, we still want to allocate
|
|
// a buffer of two bytes just to verify that FALSE and the correct
|
|
// error code is returned when we call HidD_GetFeature;
|
|
*/
|
|
|
|
if (0 == HidDevice -> HidDeviceCaps -> FeatureReportByteLength) {
|
|
featureBufferLength = 2;
|
|
}
|
|
else {
|
|
featureBufferLength = HidDevice -> HidDeviceCaps -> FeatureReportByteLength;
|
|
}
|
|
|
|
featureBuffer = (PCHAR) AllocateTestBuffer(featureBufferLength);
|
|
|
|
if (NULL == featureBuffer) {
|
|
|
|
LOG_TEST_ERROR( "Couldn't allocate memory" );
|
|
goto VERIFY_GETFEATURE_END;
|
|
|
|
}
|
|
|
|
/*
|
|
// Build the list or reportIDs that are supported
|
|
*/
|
|
|
|
returnStatus = HIDTest_BuildReportIDList( HidDevice -> HidFeatureValueCaps,
|
|
HidDevice -> HidDeviceCaps -> NumberFeatureValueCaps,
|
|
HidDevice -> HidFeatureButtonCaps,
|
|
HidDevice -> HidDeviceCaps -> NumberFeatureButtonCaps,
|
|
&reportIDList,
|
|
&reportIDListCount
|
|
);
|
|
|
|
if (!returnStatus) {
|
|
LOG_TEST_ERROR("Error building report ID list");
|
|
goto VERIFY_GETFEATURE_END;
|
|
}
|
|
|
|
/*
|
|
// Attempt to setup the device handle list, if the current
|
|
// HidDeviceHandle is not valid than we cannot proceed any farther
|
|
*/
|
|
|
|
if (IS_VALID_DEVICE_HANDLE(HidDevice -> HidDeviceHandle)) {
|
|
handleList[0] = HidDevice -> HidDeviceHandle;
|
|
|
|
ASSERT (NULL != HidDevice -> HidDeviceString);
|
|
|
|
HIDTest_InitDeviceHandles(HidDevice -> HidDeviceString,
|
|
2,
|
|
1,
|
|
handleList
|
|
);
|
|
|
|
}
|
|
else {
|
|
|
|
LOG_TEST_ERROR("HidDevice Handle is invalid -- cannot perform test");
|
|
goto VERIFY_GETFEATURE_END;
|
|
|
|
}
|
|
|
|
for (iterationCount = 1; iterationCount <= nIterations; iterationCount++) {
|
|
|
|
START_TEST_ITERATION(iterationCount);
|
|
|
|
HIDTest_ResetDeviceHandles();
|
|
|
|
while ( HIDTest_GetDeviceHandle( &currDeviceHandle, &isLegalDeviceHandle )) {
|
|
|
|
|
|
|
|
/*
|
|
// Begin by testing that we can retrieve feature reports for
|
|
// all the IDs supported on the device. For this part to
|
|
// pass:
|
|
//
|
|
// 1) returnStatus should be TRUE
|
|
// 2) errorCode should be ERROR_SUCCESS
|
|
// 3) bufferStatus should be TRUE (ie. no buffer overrun problems
|
|
//
|
|
//
|
|
// Of course, that would only happen if currDeviceHandle were
|
|
// actually a legal handle...If current device handle is not
|
|
// a legal handle, we would need to look for the following:
|
|
//
|
|
// 1) returnStatus = FALSE
|
|
// 2) errorCode = ERROR_INVALID_HANDLE (?)
|
|
// 3) bufferStatus = TRUE
|
|
*/
|
|
|
|
START_VARIATION_WITH_DEVICE_HANDLE( currDeviceHandle,
|
|
isLegalDeviceHandle,
|
|
"Getting all feature reports"
|
|
);
|
|
|
|
variationStatus = TRUE;
|
|
|
|
for (idIndex = 0; idIndex < reportIDListCount; idIndex++) {
|
|
|
|
|
|
HIDTest_CallGetFeature( currDeviceHandle,
|
|
*(reportIDList + idIndex),
|
|
featureBuffer,
|
|
featureBufferLength,
|
|
&returnStatus,
|
|
&errorCode,
|
|
&bufferStatus
|
|
);
|
|
|
|
if (!bufferStatus) {
|
|
LOG_BUFFER_VALIDATION_FAIL();
|
|
variationStatus = FALSE;
|
|
}
|
|
else if (isLegalDeviceHandle) {
|
|
|
|
if (!returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
else {
|
|
if (ERROR_SUCCESS != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
else {
|
|
if (ERROR_INVALID_HANDLE != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (variationStatus) {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
else {
|
|
LOG_VARIATION_FAIL();
|
|
failedCount++;
|
|
}
|
|
operationCount++;
|
|
|
|
END_VARIATION();
|
|
|
|
/*
|
|
// Now let's choose some report IDs that aren't in the list
|
|
// and make sure the appropriate results come back to us from
|
|
// ValidateGetFeature. Appropriate results are:
|
|
//
|
|
// if IsLegalDeviceHandle
|
|
// 1) returnStatus = FALSE
|
|
// 2) errorCode = ERROR_CRC
|
|
// 3) bufferStatus = TRUE;
|
|
//
|
|
// otherwise,
|
|
// 1) returnStatus = FALSE
|
|
// 2) errorCode = ERROR_INVALID_HANDLE
|
|
// 3) bufferStatus = TRUE;
|
|
//
|
|
// We will use the following report IDs as possible ReportID values
|
|
// to try. If any one of these report IDs is already in the list
|
|
// we'll skip it and move on to the next one.
|
|
//
|
|
// 0, 1, 2, 127, 128, 129, 254, 255
|
|
//
|
|
*/
|
|
|
|
#define BAD_REPORT_ID_LIST_COUNT 8
|
|
|
|
START_VARIATION_WITH_DEVICE_HANDLE( currDeviceHandle,
|
|
isLegalDeviceHandle,
|
|
"Attempting to retrieve non-existent report"
|
|
);
|
|
|
|
variationStatus = TRUE;
|
|
|
|
for (idIndex = 0; idIndex < BAD_REPORT_ID_LIST_COUNT; idIndex++) {
|
|
|
|
UCHAR BadReportIDList[BAD_REPORT_ID_LIST_COUNT] = { 0, 1, 127, 128, 129, 254, 255 };
|
|
|
|
if (!HIDTest_IsIDInList(BadReportIDList[idIndex],
|
|
reportIDList,
|
|
reportIDListCount
|
|
)) {
|
|
|
|
|
|
HIDTest_CallGetFeature( currDeviceHandle,
|
|
BadReportIDList[idIndex],
|
|
featureBuffer,
|
|
featureBufferLength,
|
|
&returnStatus,
|
|
&errorCode,
|
|
&bufferStatus
|
|
);
|
|
|
|
if (!bufferStatus) {
|
|
LOG_BUFFER_VALIDATION_FAIL();
|
|
variationStatus = FALSE;
|
|
}
|
|
else if (isLegalDeviceHandle) {
|
|
|
|
if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
else {
|
|
if (ERROR_CRC != errorCode &&
|
|
ERROR_INVALID_FUNCTION != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
else {
|
|
if (ERROR_INVALID_HANDLE != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (variationStatus) {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
else {
|
|
LOG_VARIATION_FAIL();
|
|
failedCount++;
|
|
}
|
|
operationCount++;
|
|
|
|
END_VARIATION();
|
|
|
|
/*
|
|
// The next step in the validation process is to pass in buffers
|
|
// that are too small to hold an entire feature report. The same
|
|
// feature buffer will be used but we'll pass in a different size
|
|
// to the ValidateFeature routine. The appropriate response to
|
|
// this call should be:
|
|
//
|
|
// 1) returnStatus = FALSE;
|
|
// 2) errorCode = ERROR_INVALID_PARAMETER (?)
|
|
// 3) bufferStatus = TRUE
|
|
//
|
|
// As above, we'll do this with two different report IDs, one
|
|
// valid one and one invalid one (if the invalid one exists) to
|
|
// make sure we hit as many possible code paths.
|
|
// Also three different lengths of buffers will be used, 1, 2,
|
|
// and featurelength-1
|
|
*/
|
|
|
|
START_VARIATION("Passing in buffers that are too small");
|
|
variationStatus = TRUE;
|
|
|
|
reportID = 0;
|
|
do {
|
|
|
|
if (!HIDTest_IsIDInList(reportID,
|
|
reportIDList,
|
|
reportIDListCount
|
|
)) {
|
|
|
|
/*
|
|
// Perform the validation steps here for each of the report
|
|
// buffer sizes
|
|
*/
|
|
|
|
reportBufferSize = 1;
|
|
|
|
while (1) {
|
|
|
|
wsprintf(varString, "ReportBufferSize = %d", reportBufferSize);
|
|
LOG_INTERMEDIATE_VARIATION_RESULT(varString);
|
|
|
|
HIDTest_CallGetFeature( currDeviceHandle,
|
|
reportID,
|
|
featureBuffer,
|
|
reportBufferSize,
|
|
&returnStatus,
|
|
&errorCode,
|
|
&bufferStatus
|
|
);
|
|
|
|
if (!bufferStatus) {
|
|
LOG_BUFFER_VALIDATION_FAIL();
|
|
variationStatus = FALSE;
|
|
}
|
|
else if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
else if (isLegalDeviceHandle) {
|
|
if (ERROR_CRC != errorCode && ERROR_INVALID_FUNCTION != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
}
|
|
else if (ERROR_INVALID_HANDLE != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
|
|
/*
|
|
// Determine the next buffer length...The buffer lengths
|
|
// that are to be used are 1,2, and featureBufferLength-1
|
|
// unless some of those happen to be the same size
|
|
*/
|
|
|
|
reportBufferSize++;
|
|
if (reportBufferSize == featureBufferLength) {
|
|
break;
|
|
}
|
|
else if (reportBufferSize > 2) {
|
|
reportBufferSize = featureBufferLength-1;
|
|
}
|
|
}
|
|
|
|
if (0 != reportID) {
|
|
break;
|
|
}
|
|
}
|
|
} while (reportID++ != 255);
|
|
}
|
|
|
|
if (variationStatus) {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
else {
|
|
LOG_VARIATION_FAIL();
|
|
failedCount++;
|
|
}
|
|
operationCount++;
|
|
|
|
END_VARIATION();
|
|
}
|
|
|
|
/*
|
|
// Do parameter validation now...Pass in the following three cases and
|
|
// verify correct operation
|
|
//
|
|
// 1) NULL buffer -- Length of 0,
|
|
// 2) NULL buffer -- Length != 0
|
|
// 3) non-NULL buffer -- length of 0
|
|
//
|
|
// Since we're verifying parameters, the actual reportID we use shouldn't
|
|
// matter, so we'll just use 1. Since we're not actually using test buffers
|
|
// we may not do any checking there either.
|
|
*/
|
|
|
|
START_VARIATION("Parameter validation of GetFeature");
|
|
variationStatus = TRUE;
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Using NULL buffer with 0 length");
|
|
|
|
HIDTest_CallGetFeature( HidDevice -> HidDeviceHandle,
|
|
1,
|
|
NULL,
|
|
0,
|
|
&returnStatus,
|
|
&errorCode,
|
|
NULL
|
|
);
|
|
|
|
if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
failedCount++;
|
|
}
|
|
else if (ERROR_INVALID_USER_BUFFER != errorCode && ERROR_INVALID_FUNCTION != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Using NULL buffer with non-zero length");
|
|
HIDTest_CallGetFeature (HidDevice -> HidDeviceHandle,
|
|
1,
|
|
NULL,
|
|
3,
|
|
&returnStatus,
|
|
&errorCode,
|
|
NULL
|
|
);
|
|
|
|
if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
else if (ERROR_NOACCESS != errorCode) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Using non-NULL buffer with zero length");
|
|
HIDTest_CallGetFeature (HidDevice -> HidDeviceHandle,
|
|
1,
|
|
featureBuffer,
|
|
0,
|
|
&returnStatus,
|
|
&errorCode,
|
|
&bufferStatus
|
|
);
|
|
|
|
if (!bufferStatus) {
|
|
LOG_BUFFER_VALIDATION_FAIL();
|
|
variationStatus = FALSE;
|
|
}
|
|
else if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
|
|
}
|
|
else if (ERROR_INVALID_USER_BUFFER != errorCode && ERROR_INVALID_FUNCTION != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
|
|
if (variationStatus) {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
else {
|
|
LOG_VARIATION_FAIL();
|
|
failedCount++;
|
|
}
|
|
operationCount++;
|
|
|
|
END_VARIATION();
|
|
|
|
VERIFY_GETFEATURE_END:
|
|
|
|
END_TEST();
|
|
|
|
if (NULL != featureBuffer) {
|
|
FreeTestBuffer(featureBuffer);
|
|
}
|
|
|
|
Status -> nOperationsPerformed = operationCount;
|
|
Status -> nOperationsPassed = operationCount - failedCount;
|
|
Status -> nOperationsFailed = failedCount;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
HIDTest_CallGetFeature(
|
|
IN HANDLE DeviceHandle,
|
|
IN UCHAR ReportID,
|
|
IN PUCHAR ReportBuffer,
|
|
IN ULONG ReportBufferLength,
|
|
OUT PBOOL ReturnStatus,
|
|
OUT PULONG ErrorCode,
|
|
OUT PBOOL BufferStatus
|
|
)
|
|
{
|
|
/*
|
|
// If we have a non-null buffer of > 0 length, then allocate the
|
|
// zero the memory and add the reportID as the first byte.
|
|
*/
|
|
|
|
if (NULL != ReportBuffer && ReportBufferLength > 0) {
|
|
ZeroMemory (ReportBuffer, ReportBufferLength);
|
|
*ReportBuffer = ReportID;
|
|
}
|
|
|
|
/*
|
|
// Call GetFeature
|
|
*/
|
|
|
|
*ReturnStatus = HidD_GetFeature(DeviceHandle,
|
|
ReportBuffer,
|
|
ReportBufferLength
|
|
);
|
|
|
|
/*
|
|
// Get the error code returned
|
|
*/
|
|
|
|
*ErrorCode = GetLastError();
|
|
|
|
/*
|
|
// Do a buffer validation check
|
|
*/
|
|
|
|
if (NULL != BufferStatus) {
|
|
*BufferStatus = ValidateTestBuffer(ReportBuffer);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
HIDTest_VerifySetFeatureA(
|
|
IN PHIDTEST_DEVICEINFO HidDevice,
|
|
IN ULONG nIterations,
|
|
OUT PHIDTEST_STATUS Status
|
|
)
|
|
{
|
|
ULONG iterationCount;
|
|
ULONG operationCount;
|
|
ULONG failedCount;
|
|
PCHAR featureBuffer;
|
|
ULONG featureBufferLength;
|
|
HANDLE handleList[MAX_NUM_HANDLES];
|
|
HANDLE currDeviceHandle;
|
|
BOOL isLegalDeviceHandle;
|
|
BOOL returnStatus;
|
|
BOOL bufferStatus;
|
|
ULONG errorCode;
|
|
PUCHAR reportIDList;
|
|
ULONG reportIDListCount;
|
|
ULONG idIndex;
|
|
UCHAR reportID;
|
|
BOOL variationStatus;
|
|
ULONG reportBufferSize;
|
|
static CHAR varString[128];
|
|
|
|
START_TEST("HIDTest_VerifySetFeature");
|
|
|
|
operationCount = 0;
|
|
failedCount = 0;
|
|
|
|
/*
|
|
// If we have no features on the device, we still want to allocate
|
|
// a buffer of two bytes just to verify that FALSE and the correct
|
|
// error code is returned when we call HidD_GetFeature;
|
|
*/
|
|
|
|
if (0 == HidDevice -> HidDeviceCaps -> FeatureReportByteLength) {
|
|
featureBufferLength = 2;
|
|
}
|
|
else {
|
|
featureBufferLength = HidDevice -> HidDeviceCaps -> FeatureReportByteLength;
|
|
}
|
|
|
|
featureBuffer = (PCHAR) AllocateTestBuffer(featureBufferLength);
|
|
|
|
if (NULL == featureBuffer) {
|
|
|
|
LOG_TEST_ERROR( "Couldn't allocate memory" );
|
|
goto VERIFY_SETFEATURE_END;
|
|
|
|
}
|
|
|
|
/*
|
|
// Build the list or reportIDs that are supported
|
|
*/
|
|
|
|
returnStatus = HIDTest_BuildReportIDList( HidDevice -> HidFeatureValueCaps,
|
|
HidDevice -> HidDeviceCaps -> NumberFeatureValueCaps,
|
|
HidDevice -> HidFeatureButtonCaps,
|
|
HidDevice -> HidDeviceCaps -> NumberFeatureButtonCaps,
|
|
&reportIDList,
|
|
&reportIDListCount
|
|
);
|
|
|
|
if (!returnStatus) {
|
|
LOG_TEST_ERROR("Error building report ID list");
|
|
goto VERIFY_SETFEATURE_END;
|
|
}
|
|
|
|
/*
|
|
// Attempt to setup the device handle list, if the current
|
|
// HidDeviceHandle is not valid than we cannot proceed any farther
|
|
*/
|
|
|
|
if (IS_VALID_DEVICE_HANDLE(HidDevice -> HidDeviceHandle)) {
|
|
handleList[0] = HidDevice -> HidDeviceHandle;
|
|
|
|
ASSERT (NULL != HidDevice -> HidDeviceString);
|
|
|
|
HIDTest_InitDeviceHandles(HidDevice -> HidDeviceString,
|
|
2,
|
|
1,
|
|
handleList
|
|
);
|
|
|
|
}
|
|
else {
|
|
|
|
LOG_TEST_ERROR("HidDevice Handle is invalid -- cannot perform test");
|
|
goto VERIFY_SETFEATURE_END;
|
|
|
|
}
|
|
|
|
for (iterationCount = 1; iterationCount <= nIterations; iterationCount++) {
|
|
|
|
START_TEST_ITERATION(iterationCount);
|
|
|
|
HIDTest_ResetDeviceHandles();
|
|
|
|
while ( HIDTest_GetDeviceHandle( &currDeviceHandle, &isLegalDeviceHandle )) {
|
|
|
|
|
|
|
|
/*
|
|
// Begin by testing that we can set feature reports for
|
|
// all the IDs supported on the device. For this part to
|
|
// pass:
|
|
//
|
|
// 1) returnStatus should be TRUE
|
|
// 2) errorCode should be ERROR_SUCCESS
|
|
// 3) bufferStatus should be TRUE (ie. no buffer overrun problems
|
|
//
|
|
//
|
|
// Of course, that would only happen if currDeviceHandle were
|
|
// actually a legal handle...If current device handle is not
|
|
// a legal handle, we would need to look for the following:
|
|
//
|
|
// 1) returnStatus = FALSE
|
|
// 2) errorCode = ERROR_INVALID_HANDLE (?)
|
|
// 3) bufferStatus = TRUE
|
|
*/
|
|
|
|
START_VARIATION_WITH_DEVICE_HANDLE( currDeviceHandle,
|
|
isLegalDeviceHandle,
|
|
"Setting all feature reports"
|
|
);
|
|
|
|
variationStatus = TRUE;
|
|
|
|
for (idIndex = 0; idIndex < reportIDListCount; idIndex++) {
|
|
|
|
|
|
HIDTest_CallSetFeature( currDeviceHandle,
|
|
*(reportIDList + idIndex),
|
|
featureBuffer,
|
|
featureBufferLength,
|
|
&returnStatus,
|
|
&errorCode,
|
|
&bufferStatus
|
|
);
|
|
|
|
if (!bufferStatus) {
|
|
LOG_BUFFER_VALIDATION_FAIL();
|
|
variationStatus = FALSE;
|
|
}
|
|
else if (isLegalDeviceHandle) {
|
|
|
|
if (!returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
else {
|
|
if (ERROR_SUCCESS != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
else {
|
|
if (ERROR_INVALID_HANDLE != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (variationStatus) {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
else {
|
|
LOG_VARIATION_FAIL();
|
|
failedCount++;
|
|
}
|
|
operationCount++;
|
|
|
|
END_VARIATION();
|
|
|
|
/*
|
|
// Now let's choose some report IDs that aren't in the list
|
|
// and make sure the appropriate results come back to us from
|
|
// CallSetFeature. Appropriate results are:
|
|
//
|
|
// if IsLegalDeviceHandle
|
|
// 1) returnStatus = FALSE
|
|
// 2) errorCode = ERROR_CRC
|
|
// 3) bufferStatus = TRUE;
|
|
//
|
|
// otherwise,
|
|
// 1) returnStatus = FALSE
|
|
// 2) errorCode = ERROR_INVALID_HANDLE
|
|
// 3) bufferStatus = TRUE;
|
|
//
|
|
// We will use the following report IDs as possible ReportID values
|
|
// to try. If any one of these report IDs is already in the list
|
|
// we'll skip it and move on to the next one.
|
|
//
|
|
// 0, 1, 2, 127, 128, 129, 254, 255
|
|
//
|
|
*/
|
|
|
|
#define BAD_REPORT_ID_LIST_COUNT 8
|
|
|
|
START_VARIATION_WITH_DEVICE_HANDLE( currDeviceHandle,
|
|
isLegalDeviceHandle,
|
|
"Attempting to set non-existent report"
|
|
);
|
|
|
|
variationStatus = TRUE;
|
|
|
|
for (idIndex = 0; idIndex < BAD_REPORT_ID_LIST_COUNT; idIndex++) {
|
|
|
|
UCHAR BadReportIDList[BAD_REPORT_ID_LIST_COUNT] = { 0, 1, 127, 128, 129, 254, 255 };
|
|
|
|
if (!HIDTest_IsIDInList(BadReportIDList[idIndex],
|
|
reportIDList,
|
|
reportIDListCount
|
|
)) {
|
|
|
|
|
|
HIDTest_CallSetFeature( currDeviceHandle,
|
|
BadReportIDList[idIndex],
|
|
featureBuffer,
|
|
featureBufferLength,
|
|
&returnStatus,
|
|
&errorCode,
|
|
&bufferStatus
|
|
);
|
|
|
|
if (!bufferStatus) {
|
|
LOG_BUFFER_VALIDATION_FAIL();
|
|
variationStatus = FALSE;
|
|
}
|
|
else if (isLegalDeviceHandle) {
|
|
|
|
if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
else {
|
|
if (ERROR_CRC != errorCode && ERROR_INVALID_FUNCTION != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
else {
|
|
if (ERROR_INVALID_HANDLE != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (variationStatus) {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
else {
|
|
LOG_VARIATION_FAIL();
|
|
failedCount++;
|
|
}
|
|
operationCount++;
|
|
|
|
END_VARIATION();
|
|
|
|
/*
|
|
// The next step in the validation process is to pass in buffers
|
|
// that are too small to hold an entire feature report. The same
|
|
// feature buffer will be used but we'll pass in a different size
|
|
// to the CallSetFeature routine. The appropriate response to
|
|
// this call should be:
|
|
//
|
|
// 1) returnStatus = FALSE;
|
|
// 2) errorCode = ERROR_CRC
|
|
// 3) bufferStatus = TRUE
|
|
//
|
|
// As above, we'll do this with two different report IDs, one
|
|
// valid one and one invalid one (if the invalid one exists) to
|
|
// make sure we hit as many possible code paths.
|
|
// Also three different lengths of buffers will be used, 1, 2,
|
|
// and featurelength-1
|
|
*/
|
|
|
|
START_VARIATION("Passing in buffers that are too small");
|
|
variationStatus = TRUE;
|
|
|
|
reportID = 0;
|
|
do {
|
|
|
|
if (!HIDTest_IsIDInList(reportID,
|
|
reportIDList,
|
|
reportIDListCount
|
|
)) {
|
|
|
|
/*
|
|
// Perform the validation steps here for each of the report
|
|
// buffer sizes
|
|
*/
|
|
|
|
reportBufferSize = 1;
|
|
|
|
while (1) {
|
|
|
|
wsprintf(varString, "ReportBufferSize = %d", reportBufferSize);
|
|
LOG_INTERMEDIATE_VARIATION_RESULT(varString);
|
|
|
|
HIDTest_CallSetFeature( currDeviceHandle,
|
|
reportID,
|
|
featureBuffer,
|
|
reportBufferSize,
|
|
&returnStatus,
|
|
&errorCode,
|
|
&bufferStatus
|
|
);
|
|
|
|
if (!bufferStatus) {
|
|
LOG_BUFFER_VALIDATION_FAIL();
|
|
variationStatus = FALSE;
|
|
}
|
|
else if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
else if (isLegalDeviceHandle) {
|
|
if (ERROR_CRC != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
}
|
|
else if (ERROR_INVALID_HANDLE != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
|
|
/*
|
|
// Determine the next buffer length...The buffer lengths
|
|
// that are to be used are 1,2, and featureBufferLength-1
|
|
// unless some of those happen to be the same size
|
|
*/
|
|
|
|
reportBufferSize++;
|
|
if (reportBufferSize == featureBufferLength) {
|
|
break;
|
|
}
|
|
else if (reportBufferSize > 2) {
|
|
reportBufferSize = featureBufferLength-1;
|
|
}
|
|
}
|
|
|
|
if (0 != reportID) {
|
|
break;
|
|
}
|
|
}
|
|
} while (reportID++ != 255);
|
|
}
|
|
|
|
if (variationStatus) {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
else {
|
|
LOG_VARIATION_FAIL();
|
|
failedCount++;
|
|
}
|
|
operationCount++;
|
|
|
|
END_VARIATION();
|
|
}
|
|
|
|
/*
|
|
// Do parameter validation now...Pass in the following three cases and
|
|
// verify correct operation
|
|
//
|
|
// 1) NULL buffer -- Length of 0,
|
|
// 2) NULL buffer -- Length != 0
|
|
// 3) non-NULL buffer -- length of 0
|
|
//
|
|
// Since we're verifying parameters, the actual reportID we use shouldn't
|
|
// matter, so we'll just use 1. Since we're not actually using test buffers
|
|
// we may not do any checking there either.
|
|
*/
|
|
|
|
START_VARIATION("Parameter validation of SetFeature");
|
|
variationStatus = TRUE;
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Using NULL buffer with 0 length");
|
|
|
|
HIDTest_CallSetFeature( HidDevice -> HidDeviceHandle,
|
|
1,
|
|
NULL,
|
|
0,
|
|
&returnStatus,
|
|
&errorCode,
|
|
NULL
|
|
);
|
|
|
|
if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
failedCount++;
|
|
}
|
|
else if (ERROR_INVALID_USER_BUFFER != errorCode && ERROR_INVALID_FUNCTION != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Using NULL buffer with non-zero length");
|
|
HIDTest_CallSetFeature (HidDevice -> HidDeviceHandle,
|
|
1,
|
|
NULL,
|
|
3,
|
|
&returnStatus,
|
|
&errorCode,
|
|
NULL
|
|
);
|
|
|
|
if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
else if (ERROR_INVALID_USER_BUFFER != errorCode && ERROR_INVALID_FUNCTION != errorCode) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
}
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Using non-NULL buffer with zero length");
|
|
HIDTest_CallSetFeature (HidDevice -> HidDeviceHandle,
|
|
1,
|
|
featureBuffer,
|
|
0,
|
|
&returnStatus,
|
|
&errorCode,
|
|
&bufferStatus
|
|
);
|
|
|
|
if (!bufferStatus) {
|
|
LOG_BUFFER_VALIDATION_FAIL();
|
|
variationStatus = FALSE;
|
|
}
|
|
else if (returnStatus) {
|
|
LOG_INVALID_RETURN_STATUS();
|
|
variationStatus = FALSE;
|
|
|
|
}
|
|
else if (ERROR_INVALID_USER_BUFFER != errorCode && ERROR_INVALID_FUNCTION != errorCode) {
|
|
LOG_INVALID_ERROR_CODE();
|
|
variationStatus = FALSE;
|
|
}
|
|
|
|
if (variationStatus) {
|
|
LOG_VARIATION_PASS();
|
|
}
|
|
else {
|
|
LOG_VARIATION_FAIL();
|
|
failedCount++;
|
|
}
|
|
operationCount++;
|
|
|
|
END_VARIATION();
|
|
|
|
VERIFY_SETFEATURE_END:
|
|
|
|
END_TEST();
|
|
|
|
if (NULL != featureBuffer) {
|
|
FreeTestBuffer(featureBuffer);
|
|
}
|
|
|
|
Status -> nOperationsPerformed = operationCount;
|
|
Status -> nOperationsPassed = operationCount - failedCount;
|
|
Status -> nOperationsFailed = failedCount;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
HIDTest_CallSetFeature(
|
|
IN HANDLE DeviceHandle,
|
|
IN UCHAR ReportID,
|
|
IN PUCHAR ReportBuffer,
|
|
IN ULONG ReportBufferLength,
|
|
OUT PBOOL ReturnStatus,
|
|
OUT PULONG ErrorCode,
|
|
OUT PBOOL BufferStatus
|
|
)
|
|
{
|
|
/*
|
|
// If we have a non-null buffer of > 0 length, then allocate the
|
|
// zero the memory and add the reportID as the first byte.
|
|
*/
|
|
|
|
if (NULL != ReportBuffer && ReportBufferLength > 0) {
|
|
ZeroMemory (ReportBuffer, ReportBufferLength);
|
|
*ReportBuffer = ReportID;
|
|
}
|
|
|
|
/*
|
|
// Call SetFeature
|
|
*/
|
|
|
|
*ReturnStatus = HidD_SetFeature(DeviceHandle,
|
|
ReportBuffer,
|
|
ReportBufferLength
|
|
);
|
|
|
|
/*
|
|
// Get the error code returned
|
|
*/
|
|
|
|
*ErrorCode = GetLastError();
|
|
|
|
/*
|
|
// Do a buffer validation check
|
|
*/
|
|
|
|
if (NULL != BufferStatus) {
|
|
*BufferStatus = ValidateTestBuffer(ReportBuffer);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL
|
|
HIDTest_ValidateNumInputBuffers(
|
|
IN HANDLE HandleToTest,
|
|
IN HANDLE SecondHandle,
|
|
IN BOOL IsFirstHandleLegal,
|
|
IN PULONG TestBuffer
|
|
)
|
|
{
|
|
BOOL testStatus;
|
|
BOOL callStatus;
|
|
BOOL prevStatus;
|
|
BOOL isLegalNewCount;
|
|
BOOL done;
|
|
ULONG firstHandleCount;
|
|
ULONG secondHandleCount;
|
|
ULONG newCount;
|
|
|
|
/*
|
|
// For each iteration of this test, we need to do the following steps
|
|
// 1) Open a second handle to the hid device
|
|
// 2) Call HidD_GetNumInputBuffers on both handles and verify > 0 if
|
|
// the first handle is legal
|
|
// 3) Verify that the buffer is not overrun
|
|
// 4) On first handle, call HidD_SetNumInputBuffers with 0, 1,
|
|
// CURRENT_BUFFER_MIN-1, CURRENT_BUFFER_MIN and a random value
|
|
// - If return value is FALSE and legal handle, insure the
|
|
// value was an expected illegal one...Verify that previous
|
|
// value hasn't changed
|
|
// - If return value is FALSE and illegal handle, good!
|
|
// - If return value is TRUE and illegal handle, bad news.
|
|
// - If return value is TRUE and a legal handle, make sure
|
|
// HidD_GetNumInputBuffers returns the value we just set
|
|
// - Verify that the second handle's original value has not changed
|
|
//
|
|
*/
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Retrieving current number of buffers");
|
|
|
|
/*
|
|
// Get the current number of buffers as reported for each device
|
|
*/
|
|
|
|
callStatus = HIDTest_GetBufferCount(HandleToTest,
|
|
TestBuffer
|
|
);
|
|
|
|
if (!callStatus && IsFirstHandleLegal) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("HidD_GetNumInputBuffers unexpectedly returned FALSE");
|
|
return (FALSE);
|
|
}
|
|
|
|
if (callStatus && !IsFirstHandleLegal) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("HidD_GetNumInputBuffers unexpectedly returned TRUE");
|
|
return (FALSE);
|
|
}
|
|
|
|
firstHandleCount = *TestBuffer;
|
|
|
|
callStatus = HIDTest_GetBufferCount(SecondHandle,
|
|
TestBuffer
|
|
);
|
|
|
|
if (!callStatus) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("HidD_GetNumInputBuffers unexpectedly returned FALSE on second handle");
|
|
return (FALSE);
|
|
}
|
|
|
|
secondHandleCount = *TestBuffer;
|
|
|
|
/*
|
|
// Verify that each of the counts is greater than or equal to the minimum
|
|
// number of buffers
|
|
*/
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Verifying the buffer count for both handles");
|
|
|
|
if (firstHandleCount < CURRENT_BUFFER_MIN || secondHandleCount < CURRENT_BUFFER_MIN) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("One of the buffer counts is less than the supposed minimum");
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
/*
|
|
// Begin looping and setting the buffer values for the first handle and
|
|
// verifying proper functionality. On each loop, also make sure that
|
|
// the second handle's value doesn't change.
|
|
*/
|
|
|
|
newCount = 0;
|
|
testStatus = TRUE;
|
|
done = FALSE;
|
|
|
|
while (!done) {
|
|
|
|
isLegalNewCount = (newCount >= CURRENT_BUFFER_MIN);
|
|
|
|
callStatus = HidD_SetNumInputBuffers(HandleToTest, newCount);
|
|
|
|
if (!callStatus) {
|
|
|
|
if (IsFirstHandleLegal && isLegalNewCount) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Could not set legal buffer value");
|
|
testStatus = FALSE;
|
|
|
|
}
|
|
else {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("HidD_SetNumInputBuffers properly returned FALSE");
|
|
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (!IsFirstHandleLegal) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("HidD_SetNumInputBuffers improperly returned TRUE");
|
|
testStatus = FALSE;
|
|
|
|
}
|
|
else if (!isLegalNewCount) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("HidD_SetNumInputBuffers set invalid value");
|
|
testStatus = FALSE;
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Now that we have attempted to set the first handle to a new value,
|
|
// we need to do some verification...There are four cases to look
|
|
// at:
|
|
// 1) callStatus = FALSE, isLegalNewCount = TRUE
|
|
// -- old value should be retained
|
|
//
|
|
// 2) callStatus = FALSE, isLegalNewCount = FALSE
|
|
// -- old value should be retained
|
|
//
|
|
// 3) callStatus = TRUE, isLegalNewCount = TRUE
|
|
// -- new value should be set
|
|
//
|
|
// 4) callStatus = TRUE, isLegalNewCount = FALSE
|
|
// -- new value should be set
|
|
//
|
|
// One other case occurred if the handle we tested wasn't even legal...
|
|
// in this case, just need to bolt.
|
|
*/
|
|
|
|
if (IsFirstHandleLegal) {
|
|
|
|
prevStatus = callStatus;
|
|
callStatus = HIDTest_GetBufferCount(HandleToTest, TestBuffer);
|
|
if (!callStatus) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("HidD_GetNumInputBuffers unexpectedly returned FALSE");
|
|
testStatus = FALSE;
|
|
|
|
}
|
|
|
|
if (prevStatus) {
|
|
|
|
if (*TestBuffer != newCount) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("New buffer value not actually set");
|
|
testStatus = FALSE;
|
|
firstHandleCount = *TestBuffer;
|
|
|
|
}
|
|
else {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("New buffer value properly set");
|
|
firstHandleCount = newCount;
|
|
|
|
}
|
|
|
|
}
|
|
else {
|
|
if (*TestBuffer != firstHandleCount) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Old buffer value not preserved on test handle");
|
|
testStatus = FALSE;
|
|
firstHandleCount = *TestBuffer;
|
|
}
|
|
else {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Old buffer value properly retained on test handle");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Verify that the second handle's buffer value didn't change
|
|
*/
|
|
|
|
callStatus = HIDTest_GetBufferCount(SecondHandle, TestBuffer);
|
|
|
|
if (!callStatus) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Could not get second handle value");
|
|
testStatus = FALSE;
|
|
|
|
}
|
|
else {
|
|
|
|
if (secondHandleCount != *TestBuffer) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Second handle buffer count not properly retained");
|
|
testStatus = FALSE;
|
|
secondHandleCount = *TestBuffer;
|
|
|
|
}
|
|
else {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Second handle buffer count properly retained");
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Determine what the next test value is
|
|
*/
|
|
|
|
newCount++;
|
|
switch (newCount) {
|
|
|
|
case 2:
|
|
if (2 < CURRENT_BUFFER_MIN-1) {
|
|
newCount = CURRENT_BUFFER_MIN-1;
|
|
}
|
|
break;
|
|
|
|
case CURRENT_BUFFER_MIN+1:
|
|
newCount = RANDOM_BUFFER_VALUE;
|
|
break;
|
|
|
|
case RANDOM_BUFFER_VALUE+1:
|
|
done = TRUE;
|
|
break;
|
|
}
|
|
|
|
} // while loop
|
|
|
|
return (testStatus);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_GetBufferCount(
|
|
IN HANDLE DeviceHandle,
|
|
IN PULONG TestBuffer
|
|
)
|
|
{
|
|
BOOL callStatus;
|
|
|
|
callStatus = HidD_GetNumInputBuffers(DeviceHandle, TestBuffer);
|
|
|
|
if (!ValidateTestBuffer(TestBuffer)) {
|
|
|
|
LOG_BUFFER_VALIDATION_FAIL();
|
|
callStatus = FALSE;
|
|
|
|
}
|
|
return (callStatus);
|
|
}
|
|
|
|
|
|
BOOL
|
|
HIDTest_ValidateStrings(
|
|
HANDLE DeviceHandle,
|
|
PLIST MasterList,
|
|
ULONG ManufacturerStringIndex,
|
|
ULONG ProductStringIndex,
|
|
ULONG SerialNumberStringIndex
|
|
)
|
|
{
|
|
|
|
BOOL CallStatus;
|
|
LIST StringList;
|
|
|
|
/*
|
|
// To validate the strings, we need to perform the following operations on
|
|
// the current device handle
|
|
//
|
|
// 1) Build a list of all the strings
|
|
// 2) Compare the passed in list with the
|
|
// 2) Retrieve the standard strings and if they exist, need to verify
|
|
// that they actually in the previously built list
|
|
// 3) If the the PreviousList list pointer is not NULL, compare the
|
|
// string list that was just built with the previous one. They
|
|
// should be the same.
|
|
*/
|
|
|
|
|
|
CallStatus = HIDTest_BuildStringList( DeviceHandle,
|
|
&StringList
|
|
);
|
|
|
|
|
|
if (!CallStatus) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Could not build string list");
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
/*
|
|
// Compare the string list just built with the master list that was
|
|
// passed in as a parameter. They should be the same
|
|
*/
|
|
|
|
if (!HIDTest_CompareStringLists(&StringList, MasterList)) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("String lists not equal");
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
/*
|
|
// Validate the standard strings (manufacturer, product, and serial number).
|
|
// checking that if a string is returned for these values, it is the same
|
|
// string as marked in the master list
|
|
*/
|
|
|
|
CallStatus = HIDTest_ValidateStdStrings(DeviceHandle,
|
|
MasterList,
|
|
ManufacturerStringIndex,
|
|
ProductStringIndex,
|
|
SerialNumberStringIndex
|
|
);
|
|
|
|
if (!CallStatus) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Could not validate the standard strings");
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_ValidateStringParams(
|
|
IN HANDLE HidDevice,
|
|
IN PLIST StringList,
|
|
IN ULONG ManufacturerStringIndex,
|
|
IN ULONG ProductStringIndex,
|
|
IN ULONG SerialNumberStringIndex
|
|
)
|
|
{
|
|
/*
|
|
// This function will perform parameter validation for each of the following
|
|
// calls:
|
|
//
|
|
// HidD_GetIndexedString();
|
|
// HidD_GetManufacturerString();
|
|
// HidD_GetProductString();
|
|
// HidD_GetSerialNumberString();
|
|
//
|
|
// It performs the following steps if the string in question actually exists
|
|
// for the passed in device:
|
|
//
|
|
// 1) Allocates a buffer of size 1 and verifies that FALSE is returned for
|
|
// the call, ERROR_INVALID_USER_BUFFER has been set and there is
|
|
// no trashing of the buffer
|
|
// 2) Allocats a buffer of size 2 and does the above check
|
|
// 3) Allocates a buffer of stringLength * sizeof(WCHAR) - 1 to verify
|
|
// the same check
|
|
// 4) Passes in a NULL buffer with a defined length of 0
|
|
// 5) Passes in a NULL buffer with a defined length != 0
|
|
*/
|
|
|
|
ULONG stringIndex;
|
|
ULONG indexValue;
|
|
PCHAR stringTypeDesc;
|
|
BOOL testStatus;
|
|
BOOL callStatus;
|
|
STRING_LIST_NODE stringNode;
|
|
PSTRING_LIST_NODE ptrStringNode;
|
|
static CHAR ErrorString[128];
|
|
|
|
ASSERT(STRING_INDEX_MANUFACTURER+1 == STRING_INDEX_PRODUCT);
|
|
ASSERT(STRING_INDEX_PRODUCT+1 == STRING_INDEX_SERIAL_NUMBER);
|
|
|
|
testStatus = TRUE;
|
|
|
|
for (stringIndex = STRING_INDEX_MANUFACTURER; stringIndex <= STRING_INDEX_SERIAL_NUMBER; stringIndex++) {
|
|
|
|
switch (stringIndex) {
|
|
case STRING_INDEX_MANUFACTURER:
|
|
indexValue = ManufacturerStringIndex;
|
|
stringTypeDesc = "Manufacturer";
|
|
break;
|
|
|
|
case STRING_INDEX_PRODUCT:
|
|
indexValue = ProductStringIndex;
|
|
stringTypeDesc = "Product";
|
|
break;
|
|
|
|
case STRING_INDEX_SERIAL_NUMBER:
|
|
indexValue = SerialNumberStringIndex;
|
|
stringTypeDesc = "Serial number";
|
|
break;
|
|
}
|
|
|
|
if (NO_STRING_INDEX == indexValue) {
|
|
|
|
wsprintf(ErrorString,
|
|
"%s string does not exist -- no parameter checks",
|
|
stringTypeDesc
|
|
);
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT( ErrorString );
|
|
|
|
}
|
|
else {
|
|
|
|
if (!HIDTest_IsStrIndexInStrList(StringList,
|
|
indexValue,
|
|
&stringNode)) {
|
|
|
|
wsprintf(ErrorString,
|
|
"%s string not in string list",
|
|
stringTypeDesc
|
|
);
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT( ErrorString );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
callStatus = HIDTest_ValidateStringIndexParams(HidDevice,
|
|
indexValue,
|
|
stringNode.StringLength,
|
|
stringTypeDesc
|
|
);
|
|
testStatus = testStatus && callStatus;
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Perform the above tests now but this time using HidD_GetIndexedString using
|
|
// the first index in the string list
|
|
*/
|
|
|
|
#ifdef O
|
|
if (IsListEmpty(StringList)) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT( "No strings in string list" );
|
|
|
|
}
|
|
else {
|
|
|
|
ptrStringNode = (PSTRING_LIST_NODE) GetListHead(StringList);
|
|
|
|
wsprintf(ErrorString,
|
|
"String index: %d",
|
|
ptrStringNode -> StringIndex
|
|
);
|
|
|
|
callStatus = HIDTest_ValidateStringIndexParams(HidDevice,
|
|
ptrStringNode -> StringIndex,
|
|
ptrStringNode -> StringLength,
|
|
ErrorString
|
|
);
|
|
|
|
testStatus = testStatus && callStatus;
|
|
}
|
|
#endif
|
|
return (testStatus);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_ValidateStringIndexParams(
|
|
IN HANDLE HidDevice,
|
|
IN ULONG StringIndex,
|
|
IN ULONG StringLength,
|
|
IN PCHAR StringTypeDesc
|
|
)
|
|
{
|
|
ULONG bufferLength;
|
|
BOOL done;
|
|
BOOL callStatus;
|
|
BOOL testStatus;
|
|
PWCHAR deviceString;
|
|
ULONG errorCode;
|
|
static CHAR ErrorString[128];
|
|
|
|
bufferLength = 1;
|
|
done = FALSE;
|
|
testStatus = FALSE;
|
|
|
|
while (!done) {
|
|
|
|
deviceString = (PWCHAR) AllocateTestBuffer(bufferLength*sizeof(WCHAR));
|
|
|
|
if (NULL == deviceString) {
|
|
|
|
wsprintf( ErrorString,
|
|
"Could not allocate space for %s string",
|
|
StringTypeDesc
|
|
);
|
|
|
|
LOG_TEST_ERROR ( ErrorString );
|
|
continue;
|
|
|
|
}
|
|
|
|
callStatus = HIDTest_GetString( HidDevice,
|
|
StringIndex,
|
|
deviceString,
|
|
bufferLength,
|
|
&errorCode
|
|
);
|
|
|
|
if (!ValidateTestBuffer(deviceString)) {
|
|
|
|
LOG_BUFFER_VALIDATION_FAIL();
|
|
|
|
testStatus = FALSE;
|
|
}
|
|
|
|
if (!callStatus) {
|
|
|
|
if (bufferLength == StringLength*sizeof(WCHAR)) {
|
|
|
|
wsprintf( ErrorString,
|
|
"Couldn't retrieve %s string with proper buffer",
|
|
StringTypeDesc
|
|
);
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT( ErrorString );
|
|
|
|
testStatus = FALSE;
|
|
|
|
}
|
|
|
|
else if (ERROR_INVALID_USER_BUFFER != GetLastError()) {
|
|
|
|
wsprintf( ErrorString,
|
|
"Invalid error value returned for %s string",
|
|
StringTypeDesc
|
|
);
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT(ErrorString);
|
|
|
|
testStatus = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (bufferLength != StringLength*sizeof(WCHAR)) {
|
|
|
|
wsprintf( ErrorString,
|
|
"Retrieved %s string with buffer too small",
|
|
StringTypeDesc
|
|
);
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT(ErrorString);
|
|
|
|
testStatus = FALSE;
|
|
|
|
}
|
|
}
|
|
|
|
FreeTestBuffer( deviceString );
|
|
|
|
/*
|
|
// Increment the buffer length and try again
|
|
*/
|
|
|
|
switch (bufferLength) {
|
|
|
|
case 1:
|
|
bufferLength = 2;
|
|
break;
|
|
|
|
case 2:
|
|
bufferLength = StringLength*sizeof(WCHAR)-1;
|
|
break;
|
|
|
|
default:
|
|
if (bufferLength >= StringLength*sizeof(WCHAR)) {
|
|
done = TRUE;
|
|
}
|
|
else {
|
|
bufferLength = StringLength*sizeof(WCHAR);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (bufferLength = 0;
|
|
bufferLength <= StringLength*sizeof(WCHAR);
|
|
bufferLength += StringLength*sizeof(WCHAR)) {
|
|
|
|
/*
|
|
// Pass in a NULL buffer with length 0 and NULL buffer
|
|
// with the appropriate size. Verify that
|
|
// FALSE/ERROR_INVALID_USER_BUFFER is returned
|
|
*/
|
|
|
|
callStatus = HIDTest_GetString(HidDevice,
|
|
StringIndex,
|
|
NULL,
|
|
bufferLength,
|
|
&errorCode
|
|
);
|
|
|
|
if (callStatus) {
|
|
|
|
wsprintf( ErrorString,
|
|
"%s string retrieved into NULL buffer with size: %d",
|
|
StringTypeDesc,
|
|
bufferLength
|
|
);
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT(ErrorString);
|
|
|
|
testStatus = FALSE;
|
|
}
|
|
else {
|
|
if (ERROR_INVALID_USER_BUFFER != GetLastError()) {
|
|
|
|
wsprintf( ErrorString,
|
|
"%s string expected ERROR_INVALID_USER_BUFFER error",
|
|
StringTypeDesc
|
|
);
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT( ErrorString );
|
|
|
|
testStatus = FALSE;
|
|
}
|
|
else {
|
|
|
|
wsprintf( ErrorString,
|
|
"%s string returned correctly with invalid buffer and size: %d",
|
|
StringTypeDesc,
|
|
bufferLength
|
|
);
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT( ErrorString );
|
|
}
|
|
}
|
|
}
|
|
return (testStatus);
|
|
}
|
|
|
|
|
|
BOOL
|
|
HIDTest_CreatePhysicalDeviceInfoA(
|
|
IN DEVICE_STRING DeviceName,
|
|
IN BOOL OpenOverlapped,
|
|
OUT PHIDTEST_DEVICEINFO DeviceInfo
|
|
)
|
|
{
|
|
PWCHAR tempString;
|
|
ULONG tempStringLength;
|
|
BOOL stringExists;
|
|
STRING_LIST_NODE stringNode;
|
|
|
|
/*
|
|
// First, fill our device info block so that we can easily determine
|
|
// what needs to be freed and what doesn't when we call
|
|
// HIDTest_FreeDeviceStructures
|
|
*/
|
|
|
|
ZeroMemory(DeviceInfo, sizeof(HIDTEST_DEVICEINFO));
|
|
|
|
/*
|
|
// If no device name exists, simply return false but at least we've
|
|
// fill our memory buffer so if the caller later tries to destroy this
|
|
// block we won't attempt to free nonexistant memory
|
|
*/
|
|
|
|
if (NULL == DeviceName) {
|
|
return (FALSE);
|
|
}
|
|
|
|
DeviceInfo -> IsPhysicalDevice = TRUE;
|
|
|
|
/*
|
|
// First thing to do is initialize the string list, so we can properly
|
|
// free it if need be.
|
|
*/
|
|
|
|
InitializeList(&DeviceInfo -> StringList);
|
|
|
|
/*
|
|
// Begin by creating space for the DEVICE_STRING
|
|
*/
|
|
|
|
DeviceInfo -> HidDeviceString = (DEVICE_STRING) HIDTest_AllocateDeviceString(lstrlen(DeviceName));
|
|
|
|
if (NULL == DeviceInfo -> HidDeviceString) {
|
|
return (FALSE);
|
|
}
|
|
|
|
CopyMemory(DeviceInfo -> HidDeviceString,
|
|
DeviceName,
|
|
(1+lstrlen(DeviceName)) * sizeof(TCHAR));
|
|
|
|
/*
|
|
// Next, we need to try to open the device handle
|
|
*/
|
|
|
|
DeviceInfo -> HidDeviceHandle = CreateFile(DeviceInfo -> HidDeviceString,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
OpenOverlapped ? FILE_FLAG_OVERLAPPED : 0,
|
|
NULL);
|
|
|
|
if (!(IS_VALID_DEVICE_HANDLE(DeviceInfo -> HidDeviceHandle))) {
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
// Next, we need to try to obtain the preparsed data for the device
|
|
*/
|
|
|
|
if (!HidD_GetPreparsedData(DeviceInfo -> HidDeviceHandle, &DeviceInfo -> HidDevicePpd)) {
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
return (FALSE);
|
|
}
|
|
|
|
DeviceInfo -> HidDeviceAttributes = (PHIDD_ATTRIBUTES) ALLOC(sizeof(HIDD_ATTRIBUTES));
|
|
|
|
if (NULL == DeviceInfo -> HidDeviceAttributes) {
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
return (FALSE);
|
|
}
|
|
|
|
if (!HidD_GetAttributes(DeviceInfo -> HidDeviceHandle, DeviceInfo -> HidDeviceAttributes)) {
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
return (FALSE);
|
|
}
|
|
|
|
if (!HIDTest_FillCommonDeviceInfo(DeviceInfo)) {
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
// Create the string list for the physical device
|
|
*/
|
|
|
|
if (!HIDTest_BuildStringList( DeviceInfo -> HidDeviceHandle,
|
|
&DeviceInfo -> StringList
|
|
)) {
|
|
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
// String list has been built, let's determine what the
|
|
// the different other strings are.
|
|
*/
|
|
|
|
/*
|
|
// Begin by turning off logging for this function...Odds
|
|
// are its not on yet, but will do so just to make sure
|
|
*/
|
|
|
|
LOG_OFF();
|
|
|
|
/*
|
|
// Initialize the indices
|
|
*/
|
|
|
|
DeviceInfo -> ManufacturerStringIndex = NO_STRING_INDEX;
|
|
DeviceInfo -> ProductStringIndex = NO_STRING_INDEX;
|
|
DeviceInfo -> SerialNumberStringIndex = NO_STRING_INDEX;
|
|
|
|
/*
|
|
// Get the manufacturer string
|
|
*/
|
|
|
|
/*
|
|
// The retrieval of the strings is broken in NT5 so this part is commented out for
|
|
// now
|
|
*/
|
|
|
|
#ifdef O
|
|
if (!HIDTest_DoesStringExist(DeviceInfo -> HidDeviceHandle,
|
|
STRING_INDEX_MANUFACTURER,
|
|
&tempString,
|
|
&tempStringLength,
|
|
&stringExists)) {
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
LOG_ON();
|
|
return (FALSE);
|
|
}
|
|
|
|
if (stringExists) {
|
|
|
|
if (!HIDTest_IsStringInStrList(&DeviceInfo -> StringList,
|
|
tempString,
|
|
tempStringLength,
|
|
&stringNode
|
|
)) {
|
|
FreeTestBuffer(tempString);
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
LOG_ON();
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
DeviceInfo -> ManufacturerStringIndex = stringNode.StringIndex;
|
|
FreeTestBuffer(tempString);
|
|
}
|
|
|
|
/*
|
|
// Get the product string
|
|
*/
|
|
|
|
if (!HIDTest_DoesStringExist(DeviceInfo -> HidDeviceHandle,
|
|
STRING_INDEX_PRODUCT,
|
|
&tempString,
|
|
&tempStringLength,
|
|
&stringExists)) {
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
LOG_ON();
|
|
return (FALSE);
|
|
}
|
|
|
|
if (stringExists) {
|
|
|
|
if (!HIDTest_IsStringInStrList(&DeviceInfo -> StringList,
|
|
tempString,
|
|
tempStringLength,
|
|
&stringNode
|
|
)) {
|
|
FreeTestBuffer(tempString);
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
LOG_ON();
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
DeviceInfo -> ProductStringIndex = stringNode.StringIndex;
|
|
FreeTestBuffer(tempString);
|
|
}
|
|
|
|
/*
|
|
// Get the serial number string
|
|
*/
|
|
|
|
if (!HIDTest_DoesStringExist(DeviceInfo -> HidDeviceHandle,
|
|
STRING_INDEX_SERIAL_NUMBER,
|
|
&tempString,
|
|
&tempStringLength,
|
|
&stringExists)) {
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
LOG_ON();
|
|
return (FALSE);
|
|
}
|
|
|
|
if (stringExists) {
|
|
|
|
if (!HIDTest_IsStringInStrList(&DeviceInfo -> StringList,
|
|
tempString,
|
|
tempStringLength,
|
|
&stringNode
|
|
)) {
|
|
FreeTestBuffer(tempString);
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
LOG_ON();
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
DeviceInfo -> SerialNumberStringIndex = stringNode.StringIndex;
|
|
FreeTestBuffer(tempString);
|
|
}
|
|
#endif
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_CreateLogicalDeviceInfoA(
|
|
IN PCHAR DevicePpd,
|
|
IN ULONG DevicePpdLength,
|
|
OUT PHIDTEST_DEVICEINFO DeviceInfo
|
|
)
|
|
{
|
|
/*
|
|
// Declare the HidD_Hello function to satisfy the compiler needing
|
|
// the declaration
|
|
*/
|
|
|
|
PULONG HidD_Hello;
|
|
|
|
/*
|
|
// First, fill our device info block so that we can easily determine
|
|
// what needs to be freed and what doesn't when we call
|
|
// HIDTest_FreeDeviceStructures
|
|
*/
|
|
|
|
ZeroMemory(DeviceInfo, sizeof(HIDTEST_DEVICEINFO));
|
|
|
|
DeviceInfo -> IsPhysicalDevice = FALSE;
|
|
|
|
/*
|
|
// A logical device has no string associated with it.
|
|
*/
|
|
|
|
DeviceInfo -> HidDeviceString = NULL;
|
|
|
|
/*
|
|
// A logical device also has no object handle, so we invalidate that
|
|
// as well.
|
|
*/
|
|
|
|
DeviceInfo -> HidDeviceHandle = INVALID_HANDLE_VALUE;
|
|
|
|
/*
|
|
// Allocate memory for our preparsed data structure. Since the input
|
|
// pointer points to a structure that is obtained at the driver level
|
|
// we need to allocate a little more space for the signature that
|
|
// is attached to driver supplied info by HID.DLL.
|
|
//
|
|
// WARNING: This is buggy as the signature prepended to the structure
|
|
// may someday changed and blow this routine to pieces. The
|
|
// following prepended header is derived from the current
|
|
// HID.DLL implementation and header files (which could change).
|
|
*/
|
|
|
|
/*
|
|
// First, let's attempt to get the address to HidD_Hello
|
|
*/
|
|
|
|
HidD_Hello = (PULONG) GetProcAddress(GetModuleHandle("HID.DLL"),
|
|
"HidD_Hello"
|
|
);
|
|
|
|
if (NULL == HidD_Hello) {
|
|
return (FALSE);
|
|
}
|
|
|
|
DeviceInfo -> HidDevicePpd = (PHIDP_PREPARSED_DATA) ALLOC((4*sizeof(ULONG)) + DevicePpdLength);
|
|
|
|
|
|
if (NULL == DeviceInfo -> HidDevicePpd) {
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
// Setup the signature on the Ppd, copy over the real Ppd struct and set
|
|
// the address of the Ppd to past the signature. Now, it's just like
|
|
// we got the data directly from the device.
|
|
*/
|
|
|
|
*((PULONG) DeviceInfo -> HidDevicePpd) = (ULONG) &HidD_Hello;
|
|
|
|
(PULONG) DeviceInfo -> HidDevicePpd = ((PULONG) DeviceInfo -> HidDevicePpd) + 4;
|
|
CopyMemory(DeviceInfo -> HidDevicePpd, DevicePpd, DevicePpdLength);
|
|
|
|
/*
|
|
// Also, a logical device has no attributes so we NULL that field as well.
|
|
*/
|
|
|
|
DeviceInfo -> HidDeviceAttributes = NULL;
|
|
|
|
if (!HIDTest_FillCommonDeviceInfo(DeviceInfo)) {
|
|
HIDTest_FreeDeviceInfoA(DeviceInfo);
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
VOID
|
|
HIDTest_FreeDeviceInfoA(
|
|
IN PHIDTEST_DEVICEINFO DeviceInfo
|
|
)
|
|
{
|
|
if (IS_VALID_DEVICE_HANDLE(DeviceInfo -> HidDeviceHandle)) {
|
|
CloseHandle(DeviceInfo -> HidDeviceHandle);
|
|
}
|
|
|
|
if (NULL != DeviceInfo -> HidDeviceString) {
|
|
HIDTest_FreeDeviceString(DeviceInfo -> HidDeviceString);
|
|
}
|
|
|
|
if (NULL != DeviceInfo -> HidDevicePpd) {
|
|
|
|
if (DeviceInfo -> IsPhysicalDevice) {
|
|
HidD_FreePreparsedData(DeviceInfo -> HidDevicePpd);
|
|
}
|
|
else {
|
|
(PULONG) DeviceInfo -> HidDevicePpd = ((PULONG) DeviceInfo -> HidDevicePpd) - 4;
|
|
FREE(DeviceInfo -> HidDevicePpd);
|
|
}
|
|
}
|
|
|
|
if (NULL != DeviceInfo -> HidDeviceAttributes) {
|
|
FREE(DeviceInfo -> HidDeviceAttributes);
|
|
}
|
|
|
|
if (NULL != DeviceInfo -> HidDeviceCaps) {
|
|
FREE(DeviceInfo -> HidDeviceCaps);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
/* Local function definitions
|
|
/*****************************************************************************/
|
|
|
|
BOOL
|
|
HIDTest_FillCommonDeviceInfo(
|
|
OUT PHIDTEST_DEVICEINFO DeviceInfo
|
|
)
|
|
{
|
|
BOOL isError;
|
|
|
|
isError = FALSE;
|
|
|
|
DeviceInfo -> HidDeviceCaps = (PHIDP_CAPS) ALLOC(sizeof(HIDP_CAPS));
|
|
|
|
if (NULL == DeviceInfo -> HidDeviceCaps) {
|
|
isError = TRUE;
|
|
goto FILL_INFO_END;
|
|
}
|
|
|
|
if (!HidP_GetCaps(DeviceInfo -> HidDevicePpd,
|
|
DeviceInfo -> HidDeviceCaps)) {
|
|
isError = TRUE;
|
|
goto FILL_INFO_END;
|
|
}
|
|
|
|
isError = !HIDTest_FillValueCaps( HidP_Input,
|
|
&DeviceInfo -> HidInputValueCaps,
|
|
DeviceInfo -> HidDeviceCaps -> NumberInputValueCaps,
|
|
DeviceInfo -> HidDevicePpd
|
|
);
|
|
|
|
isError = !HIDTest_FillValueCaps( HidP_Output,
|
|
&DeviceInfo -> HidOutputValueCaps,
|
|
DeviceInfo -> HidDeviceCaps -> NumberOutputValueCaps,
|
|
DeviceInfo -> HidDevicePpd) || isError;
|
|
|
|
isError = !HIDTest_FillValueCaps( HidP_Feature,
|
|
&DeviceInfo -> HidFeatureValueCaps,
|
|
DeviceInfo -> HidDeviceCaps -> NumberFeatureValueCaps,
|
|
DeviceInfo -> HidDevicePpd) || isError;
|
|
|
|
isError = !HIDTest_FillButtonCaps(HidP_Input,
|
|
&DeviceInfo -> HidInputButtonCaps,
|
|
DeviceInfo -> HidDeviceCaps -> NumberInputButtonCaps,
|
|
DeviceInfo -> HidDevicePpd
|
|
) || isError;
|
|
|
|
isError = !HIDTest_FillButtonCaps(HidP_Output,
|
|
&DeviceInfo -> HidOutputButtonCaps,
|
|
DeviceInfo -> HidDeviceCaps -> NumberOutputButtonCaps,
|
|
DeviceInfo -> HidDevicePpd) || isError;
|
|
|
|
isError = !HIDTest_FillButtonCaps(HidP_Feature,
|
|
&DeviceInfo -> HidFeatureButtonCaps,
|
|
DeviceInfo -> HidDeviceCaps -> NumberFeatureButtonCaps,
|
|
DeviceInfo -> HidDevicePpd) || isError;
|
|
|
|
FILL_INFO_END:
|
|
return (!isError);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_FillValueCaps(
|
|
IN HIDP_REPORT_TYPE ReportType,
|
|
IN PHIDP_VALUE_CAPS *CapsBuffer,
|
|
IN ULONG NumCaps,
|
|
IN PHIDP_PREPARSED_DATA Ppd
|
|
)
|
|
{
|
|
BOOL callStatus;
|
|
BOOL isError;
|
|
USHORT numCaps;
|
|
|
|
isError = FALSE;
|
|
|
|
if (0 != NumCaps) {
|
|
|
|
*CapsBuffer = (PHIDP_VALUE_CAPS) ALLOC(NumCaps * sizeof(HIDP_VALUE_CAPS));
|
|
|
|
if (NULL != *CapsBuffer) {
|
|
|
|
numCaps = (USHORT) NumCaps;
|
|
|
|
callStatus = HidP_GetValueCaps( ReportType,
|
|
*CapsBuffer,
|
|
&numCaps,
|
|
Ppd
|
|
);
|
|
|
|
if (HIDP_STATUS_SUCCESS != callStatus || numCaps != NumCaps) {
|
|
isError = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
isError = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
*CapsBuffer = NULL;
|
|
}
|
|
|
|
return (!isError);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_FillButtonCaps(
|
|
IN HIDP_REPORT_TYPE ReportType,
|
|
IN PHIDP_BUTTON_CAPS *CapsBuffer,
|
|
IN ULONG NumCaps,
|
|
IN PHIDP_PREPARSED_DATA Ppd
|
|
)
|
|
{
|
|
BOOL callStatus;
|
|
BOOL isError;
|
|
USHORT numCaps;
|
|
|
|
isError = FALSE;
|
|
|
|
if (0 != NumCaps) {
|
|
|
|
*CapsBuffer = (PHIDP_BUTTON_CAPS) ALLOC(NumCaps * sizeof(HIDP_BUTTON_CAPS));
|
|
|
|
if (NULL != *CapsBuffer) {
|
|
|
|
numCaps = (USHORT) NumCaps;
|
|
|
|
callStatus = HidP_GetButtonCaps( ReportType,
|
|
*CapsBuffer,
|
|
&numCaps,
|
|
Ppd
|
|
);
|
|
|
|
if (HIDP_STATUS_SUCCESS != callStatus || numCaps != NumCaps) {
|
|
isError = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
isError = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
*CapsBuffer = NULL;
|
|
}
|
|
|
|
return (!isError);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_DoGetFreePpd(
|
|
IN HANDLE HidDevice
|
|
)
|
|
{
|
|
PHIDP_PREPARSED_DATA HidDevicePpd;
|
|
BOOL CallStatus;
|
|
|
|
CallStatus = HidD_GetPreparsedData(HidDevice, &HidDevicePpd);
|
|
|
|
if (CallStatus) {
|
|
CallStatus = HidD_FreePreparsedData(HidDevicePpd);
|
|
}
|
|
|
|
return (CallStatus);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_ValidateAttributes(
|
|
IN HANDLE HidDevice,
|
|
IN PHIDD_ATTRIBUTES Attrib
|
|
)
|
|
{
|
|
PHIDD_ATTRIBUTES HidDeviceAttrib;
|
|
BOOL CallStatus;
|
|
|
|
|
|
/*
|
|
// Allocate a test buffer to ensure that there are no problems with the
|
|
// memory filling done by HidD_GetAttributes
|
|
*/
|
|
|
|
HidDeviceAttrib = (PHIDD_ATTRIBUTES) AllocateTestBuffer(sizeof(HIDD_ATTRIBUTES));
|
|
|
|
if (NULL == HidDeviceAttrib) {
|
|
|
|
LOG_TEST_ERROR("Couldn't allocate memory");
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
/*
|
|
// Attempt to get the attributes
|
|
*/
|
|
|
|
CallStatus = HidD_GetAttributes(HidDevice, HidDeviceAttrib);
|
|
|
|
/*
|
|
// Check the buffer to make sure it wasn't improperly touched
|
|
*/
|
|
|
|
if (!ValidateTestBuffer(HidDeviceAttrib)) {
|
|
|
|
LOG_BUFFER_VALIDATION_FAIL();
|
|
CallStatus = FALSE;
|
|
|
|
}
|
|
|
|
/*
|
|
// Make sure the attribute fields are correct as expected.
|
|
*/
|
|
|
|
if (CallStatus) {
|
|
CallStatus = (HidDeviceAttrib -> Size == sizeof(HIDD_ATTRIBUTES)) &&
|
|
HIDTest_CompareAttributes(HidDeviceAttrib, Attrib);
|
|
}
|
|
|
|
FreeTestBuffer(HidDeviceAttrib);
|
|
|
|
return (CallStatus);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_ValidateCaps(
|
|
IN PHIDP_PREPARSED_DATA HidPpd,
|
|
IN PHIDP_CAPS HidCaps
|
|
)
|
|
{
|
|
HIDP_CAPS Caps;
|
|
NTSTATUS CallStatus;
|
|
BOOL BoolStatus;
|
|
BOOL TestStatus;
|
|
ULONG NumberLinkCollectionNodes;
|
|
PHIDP_LINK_COLLECTION_NODE LinkCollectionNodeList;
|
|
|
|
|
|
CallStatus = HidP_GetCaps(HidPpd, &Caps);
|
|
TestStatus = (HIDP_STATUS_SUCCESS == CallStatus) && (HIDTest_CompareCaps(HidCaps, &Caps));
|
|
|
|
/*
|
|
// We won't even bother proceeding any farther. This will catch the case where
|
|
// we pass in invalid ppd.
|
|
*/
|
|
|
|
if (!TestStatus)
|
|
return (TestStatus);
|
|
|
|
/*
|
|
// Now come the real complicated part, we need to retrieve the following
|
|
// pieces of information concerning the current device
|
|
// 1) LinkCollectionNodes
|
|
// 2) ValueCaps for each report type
|
|
// 3) Button caps for each report type
|
|
// 4) Number of data indices for each report type
|
|
//
|
|
// From this information, we will verify each of the fields for its
|
|
// correctness. A comment on how each is performed will be included
|
|
// each test. These "tests" are actually variations.
|
|
*/
|
|
|
|
/*
|
|
// Check that the number of link collection nodes from
|
|
// HidP_GetLinkCollectionNodes is the same as the number
|
|
// reported in the caps structure.
|
|
*/
|
|
|
|
NumberLinkCollectionNodes = 0;
|
|
CallStatus = HidP_GetLinkCollectionNodes(NULL,
|
|
&NumberLinkCollectionNodes,
|
|
HidPpd
|
|
);
|
|
|
|
if (HIDP_STATUS_BUFFER_TOO_SMALL != CallStatus) {
|
|
LOG_UNEXPECTED_STATUS_WARNING("HidP_GetLinkCollectionNodes", CallStatus);
|
|
}
|
|
|
|
if (NumberLinkCollectionNodes != HidCaps -> NumberLinkCollectionNodes) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("LinkCollectionNode counts don't match");
|
|
TestStatus = FALSE;
|
|
}
|
|
|
|
LinkCollectionNodeList = (PHIDP_LINK_COLLECTION_NODE) ALLOC(NumberLinkCollectionNodes * sizeof(HIDP_LINK_COLLECTION_NODE));
|
|
|
|
if (NULL == LinkCollectionNodeList) {
|
|
LOG_TEST_ERROR("Couldn't allocate memory");
|
|
return (FALSE);
|
|
}
|
|
|
|
CallStatus = HidP_GetLinkCollectionNodes(LinkCollectionNodeList,
|
|
&NumberLinkCollectionNodes,
|
|
HidPpd
|
|
);
|
|
|
|
if (HIDP_STATUS_SUCCESS != CallStatus) {
|
|
LOG_TEST_ERROR("Couldn't retrieve LinkCollectionNode info");
|
|
FREE(LinkCollectionNodeList);
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
// Find the representative top-level collection in our
|
|
// LinkCollectionNodeList and verify that the UsagePage and
|
|
// Usage for the top-level collection match the same fields
|
|
// of the Caps structure. The top-level collection should
|
|
// be at index 0. We'll test this fact in the test that verifies
|
|
// the link collection node call
|
|
*/
|
|
|
|
if ((LinkCollectionNodeList -> LinkUsagePage != HidCaps -> UsagePage) &&
|
|
(LinkCollectionNodeList -> LinkUsage != HidCaps -> Usage)) {
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Top level UsagePage/Usage don't match");
|
|
TestStatus = FALSE;
|
|
}
|
|
|
|
FREE(LinkCollectionNodeList);
|
|
|
|
/*
|
|
// Add code here to validate the buffer sizes. I'm not sure actual
|
|
// validation is possible. Why? Because the size of the buffer is
|
|
// dependent on how the report descriptor defines the buffer and
|
|
// unfortunately, two four bit wide fields can either be in separate
|
|
// bytes with padding in between or squeezed into one byte with no
|
|
// padding. At Ring3 and the calls available, the app cannot differentiate
|
|
// these cases.
|
|
*/
|
|
|
|
/*
|
|
// Need to validate the number of button caps, value caps, and data indices
|
|
// for Input, Output and Feature Reports.
|
|
*/
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Validating input buffer caps");
|
|
|
|
BoolStatus = HIDTest_ValidateBufferValues(HidP_Input,
|
|
HidCaps -> NumberInputButtonCaps,
|
|
HidCaps -> NumberInputValueCaps,
|
|
HidCaps -> NumberInputDataIndices,
|
|
HidPpd
|
|
);
|
|
if (!BoolStatus) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Failed Input Buffer Validation");
|
|
TestStatus = FALSE;
|
|
}
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Validating output buffer caps");
|
|
|
|
BoolStatus = HIDTest_ValidateBufferValues(HidP_Output,
|
|
HidCaps -> NumberOutputButtonCaps,
|
|
HidCaps -> NumberOutputValueCaps,
|
|
HidCaps -> NumberOutputDataIndices,
|
|
HidPpd
|
|
);
|
|
if (!BoolStatus) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Failed Output Buffer Validation");
|
|
TestStatus = FALSE;
|
|
}
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Validating feature buffer caps");
|
|
|
|
BoolStatus = HIDTest_ValidateBufferValues(HidP_Feature,
|
|
HidCaps -> NumberFeatureButtonCaps,
|
|
HidCaps -> NumberFeatureValueCaps,
|
|
HidCaps -> NumberFeatureDataIndices,
|
|
HidPpd
|
|
);
|
|
if (!BoolStatus) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Failed Feature Buffer Validation");
|
|
TestStatus = FALSE;
|
|
}
|
|
return (TestStatus);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_ValidateBufferValues(
|
|
IN HIDP_REPORT_TYPE ReportType,
|
|
IN USHORT NumButtonCaps,
|
|
IN USHORT NumValueCaps,
|
|
IN USHORT NumDataIndices,
|
|
IN PHIDP_PREPARSED_DATA Ppd
|
|
)
|
|
{
|
|
USHORT ReportedButtonCaps;
|
|
USHORT ReportedValueCaps;
|
|
ULONG MaxDataIndex;
|
|
ULONG Index;
|
|
ULONG RangeIndex;
|
|
NTSTATUS CallStatus;
|
|
BOOL BoolStatus;
|
|
BOOL TestStatus;
|
|
PHIDP_BUTTON_CAPS ButtonCapsList;
|
|
PHIDP_BUTTON_CAPS ButtonCapsWalk;
|
|
PHIDP_VALUE_CAPS ValueCapsList;
|
|
PHIDP_VALUE_CAPS ValueCapsWalk;
|
|
DATA_INDEX_LIST DataIndexList;
|
|
|
|
/*
|
|
// In order to verify the buffer values, we need to perform the following
|
|
// things
|
|
//
|
|
// 1) Call GetButtonCaps to see how many button caps structures are reported
|
|
// 2) Call GetValueCaps to see how many value caps structures are reported
|
|
// 3) Retrieve the list of both the value caps and the button caps for
|
|
// the report type
|
|
// 4) Loop through the list and verifying that there are 0..NumDataIndices-1
|
|
// indices and no more and no less and that the data index values are
|
|
// also in that range.
|
|
*/
|
|
|
|
TestStatus = TRUE;
|
|
|
|
ReportedButtonCaps = 0;
|
|
CallStatus = HidP_GetButtonCaps(ReportType,
|
|
NULL,
|
|
&ReportedButtonCaps,
|
|
Ppd
|
|
);
|
|
|
|
/*
|
|
// There are two error conditions that we should expect here...
|
|
// 1) HIDP_STATUS_BUFFER_TOO_SMALL -- If button caps do exist
|
|
// 2) HIDP_STATUS_SUCCESS -- If no button caps exist for the report
|
|
//
|
|
// If one of these two cases doesn't arise, we need to log the warning msg.
|
|
*/
|
|
|
|
if ((ReportedButtonCaps > 0 && HIDP_STATUS_BUFFER_TOO_SMALL != CallStatus) ||
|
|
(0 == ReportedButtonCaps && HIDP_STATUS_SUCCESS != CallStatus)) {
|
|
|
|
LOG_UNEXPECTED_STATUS_WARNING("HidP_GetButtonCaps", CallStatus);
|
|
}
|
|
|
|
if (ReportedButtonCaps != NumButtonCaps) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("ButtonCaps values don't match");
|
|
TestStatus = FALSE;
|
|
}
|
|
|
|
/*
|
|
// There are two error conditions that we should expect here...
|
|
// 1) HIDP_STATUS_BUFFER_TOO_SMALL -- If value caps do exist
|
|
// 2) HIDP_STATUS_SUCCESS -- If no value exist for the report
|
|
//
|
|
// If one of these two cases doesn't arise, we need to log the warning msg.
|
|
*/
|
|
|
|
ReportedValueCaps = 0;
|
|
CallStatus = HidP_GetValueCaps(ReportType,
|
|
NULL,
|
|
&ReportedValueCaps,
|
|
Ppd
|
|
);
|
|
|
|
if ((ReportedButtonCaps > 0 && HIDP_STATUS_BUFFER_TOO_SMALL != CallStatus) ||
|
|
(0 == ReportedButtonCaps && HIDP_STATUS_SUCCESS != CallStatus)) {
|
|
|
|
LOG_UNEXPECTED_STATUS_WARNING("HidP_GetValueCaps", CallStatus);
|
|
}
|
|
|
|
if (ReportedValueCaps != NumValueCaps) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("ValueCaps values don't match");
|
|
TestStatus = FALSE;
|
|
}
|
|
|
|
/*
|
|
// Now we've got some debug only checks...This arises do to the fact that
|
|
// we will still allocate memory even if buffer size is 0, but only in
|
|
// the debug version...If there is an error due to a retail version of
|
|
// the build we don't want to scare out the user...However, we would
|
|
// like to verify in debug that HIDPARSE ain't trashing memory
|
|
*/
|
|
|
|
ButtonCapsList = NULL;
|
|
ValueCapsList = NULL;
|
|
|
|
if (0 != ReportedButtonCaps) {
|
|
|
|
/*
|
|
// Allocate space for a button caps list
|
|
*/
|
|
|
|
ButtonCapsList = (PHIDP_BUTTON_CAPS) malloc(ReportedButtonCaps * sizeof(HIDP_BUTTON_CAPS));
|
|
|
|
if (NULL == ButtonCapsList) {
|
|
LOG_TEST_ERROR("Couldn't allocate memory");
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
// Now that the lists have been allocated, let's try to retrieve those puppies
|
|
// once again. We should only get a return value of HIDP_STATUS_SUCCESS,
|
|
// any thing else would be an error.
|
|
*/
|
|
|
|
CallStatus = HidP_GetButtonCaps(ReportType,
|
|
ButtonCapsList,
|
|
&ReportedButtonCaps,
|
|
Ppd
|
|
);
|
|
|
|
if (HIDP_STATUS_SUCCESS != CallStatus) {
|
|
LOG_UNEXPECTED_STATUS_WARNING("HidP_GetButtonCaps", CallStatus);
|
|
TestStatus = FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Build the value caps list if one exists
|
|
*/
|
|
|
|
if (0 != ReportedValueCaps) {
|
|
|
|
/*
|
|
// Allocate space for a value caps list
|
|
*/
|
|
|
|
ValueCapsList = (PHIDP_VALUE_CAPS) malloc(ReportedValueCaps * sizeof(HIDP_VALUE_CAPS));
|
|
|
|
if (NULL == ValueCapsList) {
|
|
LOG_TEST_ERROR("Couldn't allocate memory");
|
|
FREE(ButtonCapsList);
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
// Ditto the above comment for ValueCaps
|
|
*/
|
|
|
|
CallStatus = HidP_GetValueCaps(ReportType,
|
|
ValueCapsList,
|
|
&ReportedValueCaps,
|
|
Ppd
|
|
);
|
|
|
|
if (HIDP_STATUS_SUCCESS != CallStatus) {
|
|
LOG_UNEXPECTED_STATUS_WARNING("HidP_GetValueCaps", CallStatus);
|
|
TestStatus = FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Time to verify the number of data indices for the buffer. To do so,
|
|
// we pass through both the button caps list and the value caps list and
|
|
// mark each one of the data indices report in that list. If all the
|
|
// all the data indices that are report is equivalent to the number of data
|
|
// indices reported in the caps structure and the appropriate data indices
|
|
// we marked, then we consider this to have passed.
|
|
*/
|
|
|
|
if (0 == NumDataIndices) {
|
|
|
|
if (ReportedButtonCaps + ReportedValueCaps > 0) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("No data indices although Button/Value Caps exist");
|
|
TestStatus = FALSE;
|
|
}
|
|
else {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("No data indices to verify");
|
|
}
|
|
}
|
|
else {
|
|
|
|
/*
|
|
// The maximum data index value should be one less than the number
|
|
// of report data indices since the data index range is 0 --> Number-1
|
|
*/
|
|
|
|
MaxDataIndex = NumDataIndices - 1;
|
|
|
|
BoolStatus = HIDTest_InitDataIndexList(MaxDataIndex,
|
|
&DataIndexList
|
|
);
|
|
|
|
if (!BoolStatus) {
|
|
LOG_TEST_ERROR("Couldn't initialize data index list");
|
|
|
|
if (NULL != ButtonCapsList) {
|
|
free(ButtonCapsList);
|
|
}
|
|
|
|
if (NULL != ValueCapsList) {
|
|
free(ValueCapsList);
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
// Begin by walking the button caps list and marking all the data indices
|
|
// reported in that list.
|
|
*/
|
|
|
|
ButtonCapsWalk = ButtonCapsList;
|
|
for (Index = 0; Index < ReportedButtonCaps; Index++, ButtonCapsWalk++) {
|
|
|
|
if (ButtonCapsWalk -> IsRange) {
|
|
for (RangeIndex = ButtonCapsWalk -> Range.DataIndexMin;
|
|
RangeIndex <= ButtonCapsWalk -> Range.DataIndexMax;
|
|
RangeIndex++) {
|
|
|
|
BoolStatus = HIDTest_MarkDataIndex(&DataIndexList,
|
|
RangeIndex
|
|
);
|
|
if (!BoolStatus) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Bad button data index");
|
|
TestStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
BoolStatus = HIDTest_MarkDataIndex(&DataIndexList,
|
|
ButtonCapsWalk -> NotRange.DataIndex
|
|
);
|
|
|
|
if (!BoolStatus) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Bad button data index");
|
|
TestStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Walk the value caps list and mark all the data indices report there
|
|
// as well.
|
|
*/
|
|
|
|
ValueCapsWalk = ValueCapsList;
|
|
for (Index = 0; Index < ReportedValueCaps; Index++, ValueCapsWalk++) {
|
|
|
|
if (ValueCapsWalk -> IsRange) {
|
|
for (RangeIndex = ValueCapsWalk -> Range.DataIndexMin;
|
|
RangeIndex <= ValueCapsWalk -> Range.DataIndexMax;
|
|
RangeIndex++) {
|
|
|
|
BoolStatus = HIDTest_MarkDataIndex(&DataIndexList,
|
|
RangeIndex
|
|
);
|
|
if (!BoolStatus) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Bad value data index");
|
|
TestStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
BoolStatus = HIDTest_MarkDataIndex(&DataIndexList,
|
|
ValueCapsWalk -> NotRange.DataIndex
|
|
);
|
|
|
|
if (!BoolStatus) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Bad value data index");
|
|
TestStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Check that all the data indices have been properly used.
|
|
*/
|
|
|
|
if (!HIDTest_AreAllIndicesUsed(&DataIndexList)) {
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Unused data indices in data index list");
|
|
TestStatus = FALSE;
|
|
}
|
|
|
|
HIDTest_FreeDataIndexList(&DataIndexList);
|
|
}
|
|
|
|
if (NULL != ButtonCapsList) {
|
|
free(ButtonCapsList);
|
|
}
|
|
|
|
if (NULL != ValueCapsList) {
|
|
free(ValueCapsList);
|
|
}
|
|
|
|
return (TestStatus);
|
|
}
|
|
|
|
|
|
BOOL
|
|
HIDTest_CompareCaps(
|
|
IN PHIDP_CAPS Caps1,
|
|
IN PHIDP_CAPS Caps2
|
|
)
|
|
{
|
|
/*
|
|
// This could very well be implemented as a macro like CompareAttributes
|
|
// was, however, there are a lot of fields to compare so easier to define
|
|
// as function
|
|
*/
|
|
|
|
return ((Caps1 -> UsagePage == Caps2 -> UsagePage) &&
|
|
(Caps1 -> Usage == Caps2 -> Usage) &&
|
|
(Caps1 -> InputReportByteLength == Caps2 -> InputReportByteLength) &&
|
|
(Caps1 -> OutputReportByteLength == Caps2 -> OutputReportByteLength) &&
|
|
(Caps1 -> FeatureReportByteLength == Caps2 -> FeatureReportByteLength) &&
|
|
(Caps1 -> NumberLinkCollectionNodes == Caps2 -> NumberLinkCollectionNodes) &&
|
|
(Caps1 -> NumberInputButtonCaps == Caps2 -> NumberInputButtonCaps) &&
|
|
(Caps1 -> NumberInputValueCaps == Caps2 -> NumberInputValueCaps) &&
|
|
(Caps1 -> NumberInputDataIndices == Caps2 -> NumberInputDataIndices) &&
|
|
(Caps1 -> NumberOutputButtonCaps == Caps2 -> NumberOutputButtonCaps) &&
|
|
(Caps1 -> NumberOutputValueCaps == Caps2 -> NumberOutputValueCaps) &&
|
|
(Caps1 -> NumberOutputDataIndices == Caps2 -> NumberOutputDataIndices) &&
|
|
(Caps1 -> NumberFeatureButtonCaps == Caps2 -> NumberFeatureButtonCaps) &&
|
|
(Caps1 -> NumberFeatureValueCaps == Caps2 -> NumberFeatureValueCaps) &&
|
|
(Caps1 -> NumberFeatureDataIndices == Caps2 -> NumberFeatureDataIndices)
|
|
);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
/* Functions related to tracking data indices
|
|
/*****************************************************************************/
|
|
BOOL
|
|
HIDTest_InitDataIndexList(
|
|
IN ULONG MaxDataIndex,
|
|
OUT PDATA_INDEX_LIST IndexList
|
|
)
|
|
{
|
|
ULONG Index;
|
|
|
|
IndexList -> List = (BOOL *) ALLOC((MaxDataIndex+1) * sizeof(BOOL));
|
|
IndexList -> MaxDataIndex = MaxDataIndex;
|
|
|
|
if (NULL != IndexList -> List) {
|
|
for (Index = 0; Index <= MaxDataIndex; Index++) {
|
|
IndexList -> List[Index] = FALSE;
|
|
}
|
|
}
|
|
return (NULL != IndexList -> List);
|
|
}
|
|
|
|
VOID
|
|
HIDTest_FreeDataIndexList(
|
|
IN PDATA_INDEX_LIST IndexList
|
|
)
|
|
{
|
|
ASSERT (NULL != IndexList -> List);
|
|
|
|
FREE(IndexList -> List);
|
|
|
|
IndexList -> List = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_MarkDataIndex(
|
|
IN PDATA_INDEX_LIST IndexList,
|
|
IN ULONG IndexValue
|
|
)
|
|
{
|
|
BOOL OldValue;
|
|
|
|
if (IndexValue > IndexList -> MaxDataIndex) {
|
|
return (FALSE);
|
|
}
|
|
|
|
OldValue = IndexList -> List[IndexValue];
|
|
|
|
IndexList -> List[IndexValue] = TRUE;
|
|
|
|
return (!OldValue);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_GetDataIndexStatus(
|
|
IN PDATA_INDEX_LIST IndexList,
|
|
IN ULONG IndexValue
|
|
)
|
|
{
|
|
ASSERT (NULL != IndexList -> List);
|
|
ASSERT (IndexValue <= IndexList -> MaxDataIndex);
|
|
|
|
return (IndexList -> List[IndexValue]);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_AreAllIndicesUsed(
|
|
IN PDATA_INDEX_LIST IndexList
|
|
)
|
|
{
|
|
ULONG Index;
|
|
|
|
ASSERT (NULL != IndexList -> List);
|
|
|
|
for (Index = 0; Index <= IndexList -> MaxDataIndex; Index++) {
|
|
if (!(IndexList -> List[Index])) {
|
|
return (FALSE);
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
/* Functions to track the availibility of strings for a device
|
|
/****************************************************************************/
|
|
BOOL
|
|
HIDTest_DoesStringExist(
|
|
HANDLE DeviceHandle,
|
|
ULONG StringIndex,
|
|
PWCHAR *String,
|
|
PULONG StringLength,
|
|
BOOL *StringExists
|
|
)
|
|
{
|
|
WCHAR TempChar;
|
|
ULONG StringSize;
|
|
BOOL CallStatus;
|
|
BOOL TestStatus;
|
|
ULONG ErrorCode;
|
|
|
|
/*
|
|
// We will use the TempChar variable first, just to determine if the
|
|
// string exists. If it HidD_GetIndexedString returns TRUE, then
|
|
// the string exists and we'll try to figure out it's actual length
|
|
// and create a buffer for it
|
|
*/
|
|
|
|
*String = NULL;
|
|
*StringLength = 0;
|
|
TestStatus = TRUE;
|
|
|
|
CallStatus = HIDTest_GetString(DeviceHandle,
|
|
StringIndex,
|
|
&TempChar,
|
|
sizeof(TempChar),
|
|
&ErrorCode
|
|
);
|
|
|
|
/*
|
|
// If the GetString call returned FALSE, we need to examine the error code
|
|
// that was returned using GetLastError(). A return of FALSE can mean
|
|
// one of the following two things.
|
|
// 1) String index not supported by the device (ERROR_GEN_FAILURE)
|
|
// 2) String index is supported by the device but a big
|
|
// enough buffer wasn't passed down (ERROR_INVALID_USER_BUFFER)
|
|
*/
|
|
|
|
if (!CallStatus) {
|
|
|
|
switch (ErrorCode) {
|
|
case ERROR_GEN_FAILURE:
|
|
|
|
/*
|
|
// This case is added here since NT5 string getting is currently
|
|
// busted
|
|
*/
|
|
|
|
case ERROR_NOACCESS:
|
|
*StringExists = FALSE;
|
|
break;
|
|
|
|
case ERROR_INVALID_USER_BUFFER:
|
|
*StringExists = TRUE;
|
|
break;
|
|
|
|
default:
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Unknown HidD_GetIndexedString error code returned");
|
|
TestStatus = FALSE;
|
|
*StringExists = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!*StringExists) {
|
|
return (TestStatus);
|
|
}
|
|
|
|
/*
|
|
// OK, so the string actually exists. Now, let's try to create a buffer
|
|
// to get the string length. Will start with 8 chararcters and double that
|
|
// size until we find a buffer which can hold the whole string
|
|
*/
|
|
|
|
StringSize = 8;
|
|
|
|
while (NULL != (*String = (PWCHAR) AllocateTestBuffer(StringSize*sizeof(WCHAR)))) {
|
|
|
|
CallStatus = HIDTest_GetString(DeviceHandle,
|
|
StringIndex,
|
|
*String,
|
|
StringSize*sizeof(WCHAR),
|
|
&ErrorCode
|
|
);
|
|
|
|
if (!ValidateTestBuffer(*String)) {
|
|
|
|
LOG_BUFFER_VALIDATION_FAIL();
|
|
FreeTestBuffer(*String);
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
if (!CallStatus) {
|
|
|
|
switch (ErrorCode) {
|
|
|
|
/*
|
|
// We simply do nothing, since the loop will end up being
|
|
// repeated and a new buffer will be allocated
|
|
*/
|
|
|
|
case ERROR_INVALID_USER_BUFFER:
|
|
break;
|
|
|
|
/*
|
|
// This error message is bad because it means a call to
|
|
// GetString passed before but now fails for some
|
|
// reason.
|
|
*/
|
|
|
|
case ERROR_GEN_FAILURE:
|
|
|
|
/*
|
|
// Ditto the above about NT5 not properly handling error status
|
|
*/
|
|
|
|
case ERROR_NOACCESS:
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Unexpected failure of string call");
|
|
TestStatus = FALSE;
|
|
break;
|
|
|
|
default:
|
|
LOG_INTERMEDIATE_VARIATION_RESULT("Unknown String error code returned");
|
|
TestStatus = FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
// We got the correct string now, however, it's still possible
|
|
// that we don't have the terminating zero. We'll call GetWideStringLength
|
|
// to determine that for us.
|
|
*/
|
|
|
|
else {
|
|
|
|
CallStatus = HIDTest_GetWideStringLength(*String, StringSize, StringLength);
|
|
|
|
/*
|
|
// If CallStatus is TRUE, then the correct string length was found
|
|
// and we can break from this loop
|
|
*/
|
|
|
|
if (CallStatus) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Otherwise, we need to free the previous buffer and allocate another
|
|
// larger buffer
|
|
*/
|
|
|
|
FreeTestBuffer(*String);
|
|
StringSize *= 2;
|
|
}
|
|
|
|
if (NULL == *String) {
|
|
|
|
LOG_TEST_ERROR("Test Error - Memory allocation failed");
|
|
TestStatus = FALSE;
|
|
|
|
}
|
|
|
|
return (TestStatus);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_GetWideStringLength(
|
|
PWCHAR String,
|
|
ULONG StringSize,
|
|
PULONG StringLength
|
|
)
|
|
{
|
|
ULONG StringIndex;
|
|
|
|
for (StringIndex = 0; StringIndex < StringSize; StringIndex++) {
|
|
|
|
if ((WCHAR) '\0' == *(String+StringIndex)) {
|
|
|
|
*StringLength = StringIndex;
|
|
return (TRUE);
|
|
}
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_BuildStringList(
|
|
HANDLE DeviceHandle,
|
|
PLIST StringList
|
|
)
|
|
{
|
|
ULONG StringIndex;
|
|
BOOL CallStatus;
|
|
PWCHAR DeviceString;
|
|
ULONG StringLength;
|
|
BOOL StringExist;
|
|
static CHAR ErrorString[128];
|
|
|
|
InitializeList(StringList);
|
|
|
|
for (StringIndex = 0; StringIndex < STRINGS_TO_TEST; StringIndex++) {
|
|
|
|
CallStatus = HIDTest_DoesStringExist(DeviceHandle,
|
|
StringIndex,
|
|
&DeviceString,
|
|
&StringLength,
|
|
&StringExist
|
|
);
|
|
|
|
if (!CallStatus) {
|
|
|
|
wsprintf(ErrorString,
|
|
"Test Error: Error determining if string %u exists",
|
|
StringIndex
|
|
);
|
|
|
|
LOG_TEST_ERROR(ErrorString);
|
|
|
|
HIDTest_FreeStringList(StringList);
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
else if (StringExist) {
|
|
|
|
CallStatus = HIDTest_AddStringToStringList(StringList,
|
|
StringIndex,
|
|
DeviceString,
|
|
StringLength
|
|
);
|
|
|
|
if (!CallStatus) {
|
|
|
|
wsprintf(ErrorString,
|
|
"Test Error: Error adding string %u to list",
|
|
StringIndex
|
|
);
|
|
|
|
LOG_TEST_ERROR(ErrorString);
|
|
|
|
FreeTestBuffer(DeviceString);
|
|
|
|
HIDTest_FreeStringList(StringList);
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
VOID
|
|
HIDTest_FreeStringList(
|
|
PLIST StringList
|
|
)
|
|
{
|
|
DestroyListWithCallback(StringList,
|
|
HIDTest_FreeStringNodeCallback
|
|
);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
HIDTest_FreeStringNodeCallback(
|
|
PLIST_NODE_HDR ListNode
|
|
)
|
|
{
|
|
PSTRING_LIST_NODE currNode;
|
|
|
|
currNode = (PSTRING_LIST_NODE) ListNode;
|
|
|
|
FreeTestBuffer(currNode -> String);
|
|
|
|
FREE(currNode);
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_AddStringToStringList(
|
|
PLIST StringList,
|
|
ULONG StringIndex,
|
|
PWCHAR String,
|
|
ULONG StringLength
|
|
)
|
|
{
|
|
PSTRING_LIST_NODE listNode;
|
|
|
|
/*
|
|
// To add a string to the string list, we need to do the following steps
|
|
// 1) If the string list is at it's maximum size or is NULL, need to
|
|
// alloc a new block
|
|
// 2) Find the end of the list and add the appropriate information there
|
|
// 3) Increment the number of strings in the list
|
|
//
|
|
// This function will assume that string indices will be added in increasing
|
|
// numerical order so that no sorting routine will have to be implemented
|
|
// This is a legitimate assumption since the BuildStringList begins with
|
|
// string zero and searchs through it's maximum for strings. Since this
|
|
// is the only caller of the AddStringToStringList, it will only add
|
|
// string indices in increasing order. If this assumption needs to change,
|
|
// it will require a little more computation to keep the list sorted
|
|
*/
|
|
|
|
listNode = (PSTRING_LIST_NODE) ALLOC(sizeof(STRING_LIST_NODE));
|
|
if (NULL == listNode) {
|
|
return (FALSE);
|
|
}
|
|
|
|
listNode -> StringIndex = StringIndex;
|
|
listNode -> String = String;
|
|
listNode -> StringLength = StringLength;
|
|
|
|
InsertTail(StringList, listNode);
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_IsStringInStrList(
|
|
PLIST StringList,
|
|
PWCHAR String,
|
|
ULONG StringLength,
|
|
PSTRING_LIST_NODE StringNode
|
|
)
|
|
{
|
|
/*
|
|
// Searching the string list for a given string is not as easy as searching
|
|
// the string list for a given string index since the strings are stored in
|
|
// in index order not alphabetical order. Therefore, we cannot perform
|
|
// any sort of binary search to deal with this case and must linearly
|
|
// search the list before we can determine that a given string does
|
|
// not exist in the list
|
|
*/
|
|
|
|
PSTRING_LIST_NODE currNode;
|
|
|
|
currNode = (PSTRING_LIST_NODE) GetListHead(StringList);
|
|
|
|
while (currNode != (PSTRING_LIST_NODE) StringList) {
|
|
|
|
if (HIDTest_CompareStrings(currNode -> String,
|
|
currNode -> StringLength,
|
|
String,
|
|
StringLength
|
|
)) {
|
|
*StringNode = *currNode;
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
currNode = (PSTRING_LIST_NODE) GetNextEntry(currNode);
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_IsStrIndexInStrList(
|
|
PLIST StringList,
|
|
ULONG StringIndex,
|
|
PSTRING_LIST_NODE StringNode
|
|
)
|
|
{
|
|
PSTRING_LIST_NODE currNode;
|
|
|
|
currNode = (PSTRING_LIST_NODE) GetListHead(StringList);
|
|
|
|
while (currNode != (PSTRING_LIST_NODE) StringList) {
|
|
|
|
if (currNode -> StringIndex == StringIndex) {
|
|
|
|
*StringNode = *currNode;
|
|
return (TRUE);
|
|
|
|
}
|
|
else if (currNode -> StringIndex > StringIndex) {
|
|
return (FALSE);
|
|
}
|
|
|
|
currNode = (PSTRING_LIST_NODE) GetNextEntry(currNode);
|
|
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_CompareStringLists(
|
|
PLIST StringList1,
|
|
PLIST StringList2
|
|
)
|
|
{
|
|
PSTRING_LIST_NODE node1;
|
|
PSTRING_LIST_NODE node2;
|
|
|
|
/*
|
|
// Comparing two string lists is a relatively simple process since we
|
|
// are only looking for whether they are equal and not where they differ.
|
|
// Plus, our string lists should both be in index string order.
|
|
//
|
|
// To do the comparison, all that needs to be done is traverse both
|
|
// lists from the beginning. If at any point, either the indices
|
|
// or the string values don't match, we can return FALSE
|
|
*/
|
|
|
|
node1 = (PSTRING_LIST_NODE) GetListHead(StringList1);
|
|
node2 = (PSTRING_LIST_NODE) GetListHead(StringList2);
|
|
|
|
while (node1 != (PSTRING_LIST_NODE) StringList1 && node2 != (PSTRING_LIST_NODE) StringList2) {
|
|
|
|
if ((node1 -> StringIndex != node2 -> StringIndex) ||
|
|
(!HIDTest_CompareStrings(node1 -> String,
|
|
node1 -> StringLength,
|
|
node2 -> String,
|
|
node2 -> StringLength
|
|
))) {
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
node1 = (PSTRING_LIST_NODE) GetNextEntry(node1);
|
|
node2 = (PSTRING_LIST_NODE) GetNextEntry(node2);
|
|
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
HIDTest_ValidateStdStrings(
|
|
HANDLE DeviceHandle,
|
|
PLIST CurrentStringList,
|
|
ULONG ManufacturerStringIndex,
|
|
ULONG ProductStringIndex,
|
|
ULONG SerialNumberStringIndex
|
|
)
|
|
{
|
|
PWCHAR DeviceString;
|
|
ULONG StringLength;
|
|
BOOL TestStatus;
|
|
BOOL CallStatus;
|
|
PCHAR StringTypeDesc;
|
|
ULONG StringIndex;
|
|
BOOL StringExist;
|
|
ULONG indexValue;
|
|
static CHAR ErrorString[128];
|
|
STRING_LIST_NODE stringNode;
|
|
|
|
/*
|
|
// To verify the standard strings, we need to the following steps
|
|
// for each of the strings (manufacturer, product, serial number)
|
|
//
|
|
// 1) Attempt to retrieve the string...
|
|
// 2) If the string doesn't exist, need to verify that the passed
|
|
// in StringIndex is == NO_STRING_INDEX
|
|
// 3) If the string exists, need to search the current string list
|
|
// to get the index and then compare that index value with the
|
|
// index value for the given string
|
|
*/
|
|
|
|
/*
|
|
// Begin with the manufacturer string and work our way up from there
|
|
// The following asserts verify that the macros that have been defined
|
|
// above are still in numerical order
|
|
*/
|
|
|
|
ASSERT(STRING_INDEX_MANUFACTURER+1 == STRING_INDEX_PRODUCT);
|
|
ASSERT(STRING_INDEX_PRODUCT+1 == STRING_INDEX_SERIAL_NUMBER);
|
|
|
|
TestStatus = TRUE;
|
|
for (StringIndex = STRING_INDEX_MANUFACTURER; StringIndex <= STRING_INDEX_SERIAL_NUMBER; StringIndex++) {
|
|
|
|
CallStatus = HIDTest_DoesStringExist(DeviceHandle,
|
|
StringIndex,
|
|
&DeviceString,
|
|
&StringLength,
|
|
&StringExist
|
|
);
|
|
switch (StringIndex) {
|
|
case STRING_INDEX_MANUFACTURER:
|
|
indexValue = ManufacturerStringIndex;
|
|
StringTypeDesc = "Manufacturer";
|
|
break;
|
|
|
|
case STRING_INDEX_PRODUCT:
|
|
indexValue = ProductStringIndex;
|
|
StringTypeDesc = "Product";
|
|
break;
|
|
|
|
case STRING_INDEX_SERIAL_NUMBER:
|
|
indexValue = SerialNumberStringIndex;
|
|
StringTypeDesc = "Serial number";
|
|
break;
|
|
}
|
|
|
|
if (!CallStatus) {
|
|
|
|
wsprintf(ErrorString,
|
|
"Test Error: Error determining if string %u exists",
|
|
StringIndex
|
|
);
|
|
|
|
LOG_TEST_ERROR(ErrorString);
|
|
|
|
return (FALSE);
|
|
}
|
|
else if (!StringExist) {
|
|
|
|
if (indexValue != NO_STRING_INDEX) {
|
|
|
|
wsprintf(ErrorString, "Mismatch: %s string not returned", StringTypeDesc);
|
|
|
|
TestStatus = FALSE;
|
|
|
|
}
|
|
else {
|
|
|
|
wsprintf(ErrorString, "%s string not found", StringTypeDesc);
|
|
}
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT(ErrorString);
|
|
|
|
}
|
|
else {
|
|
|
|
CallStatus = HIDTest_IsStringInStrList(CurrentStringList,
|
|
DeviceString,
|
|
StringLength,
|
|
&stringNode
|
|
);
|
|
|
|
if (!CallStatus) {
|
|
|
|
wsprintf(ErrorString, "%s string could not be found in string list", StringTypeDesc);
|
|
|
|
LOG_INTERMEDIATE_VARIATION_RESULT(ErrorString);
|
|
|
|
TestStatus = FALSE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (indexValue != stringNode.StringIndex) {
|
|
|
|
wsprintf(ErrorString, "Mismatched index: %s string", StringTypeDesc);
|
|
|
|
TestStatus = FALSE;
|
|
|
|
}
|
|
else {
|
|
|
|
wsprintf(ErrorString, "%s string found in the string list", StringTypeDesc);
|
|
|
|
}
|
|
LOG_INTERMEDIATE_VARIATION_RESULT(ErrorString);
|
|
}
|
|
FreeTestBuffer(DeviceString);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
return (TestStatus);
|
|
}
|
|
|
|
|
|
BOOL
|
|
HIDTest_GetString(
|
|
HANDLE DeviceHandle,
|
|
ULONG StringIndex,
|
|
PWCHAR StringBuffer,
|
|
ULONG BufferLength,
|
|
PULONG ErrorCode
|
|
)
|
|
{
|
|
BOOL CallStatus;
|
|
|
|
switch (StringIndex) {
|
|
case STRING_INDEX_MANUFACTURER:
|
|
CallStatus = HidD_GetManufacturerString(DeviceHandle,
|
|
StringBuffer,
|
|
BufferLength
|
|
);
|
|
|
|
break;
|
|
|
|
case STRING_INDEX_PRODUCT:
|
|
CallStatus = HidD_GetProductString(DeviceHandle,
|
|
StringBuffer,
|
|
BufferLength
|
|
);
|
|
|
|
break;
|
|
|
|
case STRING_INDEX_SERIAL_NUMBER:
|
|
CallStatus = HidD_GetSerialNumberString(DeviceHandle,
|
|
StringBuffer,
|
|
BufferLength
|
|
);
|
|
break;
|
|
|
|
default:
|
|
CallStatus = HidD_GetIndexedString(DeviceHandle,
|
|
StringIndex,
|
|
StringBuffer,
|
|
BufferLength
|
|
);
|
|
}
|
|
|
|
*ErrorCode = GetLastError();
|
|
return (CallStatus);
|
|
}
|
|
|
|
BOOL
|
|
HIDTest_BuildReportIDList(
|
|
IN PHIDP_VALUE_CAPS VCaps,
|
|
IN ULONG NumVCaps,
|
|
IN PHIDP_BUTTON_CAPS BCaps,
|
|
IN ULONG NumBCaps,
|
|
IN PUCHAR *ReportIDs,
|
|
IN PULONG ReportIDCount
|
|
)
|
|
{
|
|
BOOL usingReportIDs;
|
|
ULONG maxReportIDs;
|
|
|
|
usingReportIDs = FALSE;
|
|
*ReportIDCount = 0;
|
|
|
|
if (0 != NumVCaps) {
|
|
usingReportIDs = VCaps -> ReportID != 0;
|
|
}
|
|
else if (0 != NumBCaps) {
|
|
usingReportIDs = BCaps -> ReportID != 0;
|
|
}
|
|
else {
|
|
|
|
/*
|
|
// We're not actually using report IDs in the is case, but it will
|
|
// get set to TRUE anyway because both lists are empty. Doing so,
|
|
// will cause us to dump out of the routine early.
|
|
*/
|
|
|
|
usingReportIDs = TRUE;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
// We'll allocate a buffer that can hold the maximum number of report
|
|
// IDs that can possible exist for these values. Most likely, we'll
|
|
// allocate a buffer that is too big. This can be optimized later if
|
|
// necessary.
|
|
*/
|
|
|
|
if (usingReportIDs) {
|
|
maxReportIDs = NumVCaps + NumBCaps;
|
|
}
|
|
else {
|
|
maxReportIDs = 1;
|
|
}
|
|
|
|
if (0 == maxReportIDs) {
|
|
|
|
*ReportIDs = NULL;
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
*ReportIDs = (PUCHAR) malloc (maxReportIDs * sizeof(UCHAR));
|
|
|
|
if (NULL == *ReportIDs) {
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
// We've allocated the buffer, now let's fill it.
|
|
// To do so, we need to trace through each of the lists, get the
|
|
// report ID, search the current list. If found in the current list of
|
|
// IDs, we ignore and proceed to the next. Otherwise, we need to bump
|
|
// all the other IDs that are greater than this one down one spot in the
|
|
// buffer and add this new ID. Doing so will guarantee that the buffer
|
|
// we are creating will be in sorted order.
|
|
*/
|
|
|
|
while (NumVCaps--) {
|
|
|
|
HIDTest_InsertIDIntoList(*ReportIDs, ReportIDCount, VCaps -> ReportID);
|
|
VCaps++;
|
|
|
|
}
|
|
|
|
|
|
while (NumBCaps--) {
|
|
|
|
HIDTest_InsertIDIntoList(*ReportIDs, ReportIDCount, BCaps -> ReportID);
|
|
BCaps++;
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
VOID
|
|
HIDTest_InsertIDIntoList(
|
|
IN PUCHAR ReportIDs,
|
|
IN PULONG ReportIDCount,
|
|
IN UCHAR NewID
|
|
)
|
|
{
|
|
UCHAR listReportID;
|
|
ULONG listIndex;
|
|
BOOL insertIntoList;
|
|
|
|
for (listIndex = 0, insertIntoList = TRUE; listIndex < *ReportIDCount; listIndex++) {
|
|
|
|
listReportID = *(ReportIDs + listIndex);
|
|
|
|
if (listReportID == NewID) {
|
|
insertIntoList = FALSE;
|
|
break;
|
|
}
|
|
else if (listReportID > NewID) {
|
|
memmove(ReportIDs + listIndex + 1,
|
|
ReportIDs + listIndex,
|
|
(*ReportIDCount) - listIndex * sizeof(UCHAR)
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (insertIntoList) {
|
|
|
|
*(ReportIDs + listIndex) = NewID;
|
|
(*ReportIDCount)++;
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL
|
|
HIDTest_IsIDInList(
|
|
IN UCHAR ReportID,
|
|
IN PUCHAR ReportIDList,
|
|
IN ULONG ReportIDListCount
|
|
)
|
|
{
|
|
ULONG idIndex;
|
|
BOOL found;
|
|
|
|
found = FALSE;
|
|
for (idIndex = 0; idIndex < ReportIDListCount; idIndex++) {
|
|
|
|
if (ReportID == *(ReportIDList+idIndex)) {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
else if (ReportID < *(ReportIDList+idIndex)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (found);
|
|
}
|
|
|