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