windows-nt/Source/XPSP1/NT/drivers/wdm/input/client/hidtest/hidtest.c
2020-09-26 16:20:57 +08:00

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);
}