/*++ Copyright (c) 1996 Microsoft Corporation Module Name: usbtest.c Abstract: USB Test DLL Author: Chris Robinson Environment: Kernel mode Revision History: --*/ //***************************************************************************** // I N C L U D E S //***************************************************************************** #include #include #include #include #include #include #include #include #include #define INITGUID #include "usbhlfil.h" #include "usbtest.h" #include "usbtsys.h" #include "local.h" //***************************************************************************** // G L O B A L S P R I V A T E T O T H I S F I L E //***************************************************************************** // // Handle to the USBTestDriver (USBTEST.SYS). This handle is unique per // process that loads this DLL and is opened automatically when the process // attachs to the DLL. // static HANDLE USBTestHandle = INVALID_HANDLE_VALUE; //***************************************************************************** // // Entry32() // //***************************************************************************** STDAPI_ (BOOL) Entry32( HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved ) /*++ Routine Description: Entry point for the DLL system. Arguments: hInst - Handle to the instance of the DLL dwReason - Reason for the entry point being called Return Value: TRUE if SUCCESSFUL FALSE otherwise --*/ { BOOLEAN success; switch (dwReason) { // // When a process attachs to the DLL, start the USBTestService // and then get a handle to it. If the service is already // started, then there are no problems. We use this handle in // further subsequent calls that require the USBTest.sys. A process // can also explicitly start and stop the test driver as well usually // to load a new version of the driver. Whether the driver gets // successfully started, we will return TRUE but those functions // that rely on the driver will fail in the future. // case DLL_PROCESS_ATTACH: success = StartUSBTest(); break; // // On process detach, close the handle and attempt to stop the // test service. The service may not get stopped since there // may be other open handles to the device in other processes but // there is no way we can know whether this is true. We'll return // TRUE no matter what however. // case DLL_PROCESS_DETACH: success = StopUSBTest(); break; default: break; } return (TRUE); } //***************************************************************************** // // GetHubLowerFilterHandle() // //***************************************************************************** HANDLE __stdcall GetHubLowerFilterHandle( IN PCHAR HubInstanceName ) /*++ Routine Description: Returns a handle to the usb hub lower filter driver if it is loaded for the given instance of the hub. Arguments: HubInstanceName - Symbolic link name for the hub for which the caller would like a lower hub filter handle to Return Value: If successful, a handle to the hub lower filter driver In not, INVALID_HANDLE_VALUE --*/ { PCHAR filterLinks; PCHAR filterLinkWalk; BOOLEAN success; BOOLEAN found; BOOLEAN wasError; HANDLE handle; WCHAR filterHubNameW[512]; PCHAR filterHubNameA; ULONG bytesReturned; // // Build a list of usbhub lower filter driver symbolic link names // wasError = !FindDevicesByClass((LPGUID) &GUID_CLASS_USBHLFIL, &filterLinks); // // Initialize the variables for walking this list of links // found = FALSE; filterLinkWalk = filterLinks; // // Search until we encounter an error, hit the end of the list, or // find the symbolic link in the list // while (!wasError && *filterLinkWalk != '\0' && !found) { // // Open a handle to the hub filter // handle = CreateFile(filterLinkWalk, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == handle) { wasError = TRUE; continue; } // // Get the name of the hub from the hub driver...Currently // only using a static buffer to hold the name // success = DeviceIoControl(handle, IOCTL_GET_HUB_NAME, filterHubNameW, 512*sizeof(WCHAR), filterHubNameW, 512*sizeof(WCHAR), &bytesReturned, NULL); if (!success) { { DWORD error; error = GetLastError(); } wasError = TRUE; continue; } // // Convert that hub name which is unicode to an ascii string // filterHubNameA = WideStrToMultiStr(filterHubNameW); if (NULL == filterHubNameA) { wasError = TRUE; CloseHandle(handle); continue; } // // Compare the passed in string with the name of the current // hub being examined // found = (0 == strcmp((PCHAR) filterHubNameA, HubInstanceName)); if (!found) { CloseHandle(handle); } // // Continue to the next link... // filterLinkWalk += (strlen(filterLinkWalk)+1); } // // Free up the links // if (NULL != filterLinks) { GlobalFree(filterLinks); } // // Return the appropriate value // return (found ? handle : INVALID_HANDLE_VALUE); } //***************************************************************************** // // FindDevicesByClass() // //***************************************************************************** BOOLEAN __stdcall FindDevicesByClass( IN CONST GUID *ClassGuid, IN PCHAR *SymbolicLinks ) /*++ Routine Description: Builds a list of symbolic links to devices that have exposed interfaces of a specified class type Arguments: ClassGuid - The class guid of the device the caller is interested in getting links for SymbolicLinks - A function allocated structure that contains the a list of symbolic links for all devices found on the system. The caller is responsible for freeing this list. This list is terminated by a second NULL character following the last symbolic link. Return Value: If successful, TRUE and SymbolicLinks points to a valid memory structure In unsuccessful, FALSE and SymbolicLinks is NULL --*/ { PSP_DEVICE_INTERFACE_DETAIL_DATA functionClassDeviceData; HDEVINFO hardwareDeviceInfo; SP_DEVICE_INTERFACE_DATA deviceInfoData; PCHAR newSymLinks; PCHAR symLinks; ULONG symLinkSize; ULONG devInstance; ULONG requiredSize; ULONG linkSize; BOOLEAN success; BOOLEAN wasError; // // Look for all the devices on the system of the specified class // hardwareDeviceInfo = SetupDiGetClassDevs( ClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (INVALID_HANDLE_VALUE == hardwareDeviceInfo) { symLinks = NULL; wasError = TRUE; goto FindDevicesByClass_Exit; } // // Get the more specific info for each of these devices. We don't // know how many devices so we need to loop until GetLastError() // returns ERROR_NO_MORE_ITEMS; // deviceInfoData.cbSize = sizeof(deviceInfoData); // // Intialize the device instance to get the first device instance // This will be incremented at the end of the loop for the next // iteration // devInstance = 0; // // Setup the symbol link information structures. The initial // structure of strings is NULL. symLinkSize is biased to one character // however so that we always allocate enough space for the terminating // extra NULL character; symLinks = NULL; symLinkSize = sizeof(*symLinks); // // Initialize the wasError variable...If this is still true once we // leave the loop then there were no errors during the processing of the // loop // wasError = FALSE; while (1) { // // Try to get the information on the next device of the device class // success = SetupDiEnumDeviceInterfaces(hardwareDeviceInfo, 0, ClassGuid, devInstance, &deviceInfoData); // // Break out of the loop if the above function call failed. // if (!success) { wasError = (ERROR_NO_MORE_ITEMS != GetLastError()); break; } // // If a dev instance was successfully retrieved, get the symbolic // link for the string and add it to our list of symbolic links // // // Get the length of the structure required to hold the detail // data which includes the device path (ie. symbolic link). // success = SetupDiGetDeviceInterfaceDetail(hardwareDeviceInfo, &deviceInfoData, NULL, 0, &requiredSize, NULL); // // Expect a return value of FALSE but also an error code // indicating the buffer is too small. If this, isn't the // case break out of the loop // if (!success && ERROR_INSUFFICIENT_BUFFER != GetLastError()) { wasError = TRUE; break; } // // Allocate space for the detailed info now that we know just how // big it needs to be. // functionClassDeviceData = ALLOC(requiredSize); if (NULL == functionClassDeviceData) { wasError = TRUE; break; } // // Call the function again...This time, we should complete with success // functionClassDeviceData -> cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); success = SetupDiGetDeviceInterfaceDetail(hardwareDeviceInfo, &deviceInfoData, functionClassDeviceData, requiredSize, NULL, NULL); if (!success) { FREE(functionClassDeviceData); wasError = TRUE; break; } // // Add the DevicePath in this new device to our list of symbolic links // // // Get the size of the newest link that we found // linkSize = requiredSize - offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA, DevicePath); // // Allocate a new structure that is the size of the previous links // plus the new one // newSymLinks = GlobalAlloc(GPTR, symLinkSize+linkSize); if (NULL == newSymLinks) { wasError = TRUE; GlobalFree(functionClassDeviceData); break; } // // Copy the old symbolic links we had stored if they did exist and // free the old structure // if (NULL != symLinks) { CopyMemory(newSymLinks, symLinks, symLinkSize); GlobalFree(symLinks); } // // Save the new structure // symLinks = newSymLinks; // // Copy the newest link just past the end of our old links // Remember that symLinkSize was biased to 1 character initially. // and that this value has not been incremented yet to // take into account the new link // CopyMemory((symLinks + symLinkSize-sizeof(*symLinks)), functionClassDeviceData -> DevicePath, linkSize); // // OK, add the size of the new link to the total size count // symLinkSize += linkSize; // // Free the function data // FREE(functionClassDeviceData); // // Increment the device instance and loop again // devInstance++; } // // If there was an error, cleanup any symbol links that may have been // stored to this point. // if (wasError) { if (NULL != symLinks) { GlobalFree(symLinks); symLinks = NULL; } goto FindDevicesByClass_Exit; } // // The last thing that needs to be done is adding the terminating // extra NULL character // *(symLinks+(symLinkSize/sizeof(*symLinks))) = '\0'; FindDevicesByClass_Exit: *SymbolicLinks = symLinks; if (INVALID_HANDLE_VALUE != hardwareDeviceInfo) { SetupDiDestroyDeviceInfoList(hardwareDeviceInfo); } return (!wasError); } //***************************************************************************** // // BuildUSBDeviceTree() // //***************************************************************************** PUSB_DEVICE_TREE __stdcall BuildUSBDeviceTree( VOID ) /*++ Routine Description: Builds a memory representation of the current USB device tree by calling the IOCTLs of the usb hub driver that are visible to user-mode. Each node that is created has a type (host controller, hub, device, etc.) and pointers to its child nodes if they exist. The other functions in this DLL make use of this tree structure. The caller of this function should call DestroyUSBDeviceTree() when the allocated tree structure is no longer needed. Arguments: Return Value: If successful, a pointer to a USB_DEVICE_TREE In unsuccessful, NULL is returned and GetLastError() will have a meaningful error code. --*/ { PUSB_DEVICE_TREE root; PUSB_DEVICE_NODE hcNode; HANDLE hcHandle; DEVINST hcInstance; PCHAR hcName; PCHAR hcDesc; ULONG index; ULONG errorCode; BOOL success; // // Initialize error code to NO_ERROR...If, upon leaving the function, this // code is anything else, it indicates an error occurred somewhere in the // function // errorCode = ERROR_SUCCESS; // // Allocate the root node for the tree // root = CreateUSBDeviceNode(Computer, MAXIMUM_ROOT_HUBS); if (NULL == root) { errorCode = ERROR_OUTOFMEMORY; goto BuildUSBDeviceTree_Exit; } root -> NumberOfChildren = 0; root -> Description = ALLOC(sizeof("My Computer")); if (NULL != root -> Description) { strcpy(root -> Description, "My Computer"); } else { errorCode = ERROR_OUTOFMEMORY; goto BuildUSBDeviceTree_Exit; } // // Create HC nodes for all the host controllers on the system. // for (index = 0; index < MAXIMUM_ROOT_HUBS; index++) { // // Try to open the next host controller // // // Create the host controller name for this instance // hcName = ALLOC(HC_NAME_LENGTH); if (NULL == hcName) { errorCode = ERROR_OUTOFMEMORY; goto BuildUSBDeviceTree_Exit; } wsprintf(hcName, "\\\\.\\HCD%d", index); // // Open a handle to the host controller // hcHandle = CreateFile(hcName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); // // If the CreateFile failed, then just forego the rest of the loop // and try the next host controller // if (INVALID_HANDLE_VALUE == hcHandle) { FREE(hcName); continue; } // // Get the description for this device // hcInstance = GetHCDDeviceInstance(hcHandle); // // Create the host controller node // hcNode = CreateUSBDeviceNode(HostController, 1); if (NULL == hcNode) { // // Do some cleanup of things that have already been allocated // CloseHandle(hcHandle); FREE(hcName); errorCode = ERROR_OUTOFMEMORY; goto BuildUSBDeviceTree_Exit; } // // Save the node information into root computer structure // hcNode -> Parent = root; hcNode -> HCHandle = hcHandle; hcNode -> DeviceInstance = hcInstance; hcNode -> NumberOfChildren = 0; hcNode -> SymbolicLink = hcName; success = GetDeviceInfo(hcInstance, FALSE, hcNode); if (!success) { DestroyUSBDeviceNode(hcNode, NULL); errorCode = ERROR_OUTOFMEMORY; } // // Enumerate the devices attached to this host controller // success = EnumerateHostController(hcNode); if (!success) { errorCode = GetLastError(); DestroyUSBDeviceNode(hcNode, NULL); goto BuildUSBDeviceTree_Exit; } root -> Children[root -> NumberOfChildren] = hcNode; root -> NumberOfChildren++; } BuildUSBDeviceTree_Exit: if (ERROR_SUCCESS != errorCode) { if (NULL != root) { DestroyUSBDeviceTree(root); } root = NULL; } SetLastError(errorCode); return (root); } //***************************************************************************** // // CompareUSBDeviceTrees() // //***************************************************************************** BOOLEAN __stdcall CompareUSBDeviceTrees( IN PUSB_DEVICE_TREE Tree1, IN PUSB_DEVICE_TREE Tree2, OUT PULONG NumberDifferences, OUT PNODE_PAIR_LIST NodePairList ) /*++ Routine Description: Compares to USB devices that have been created. It will note how many nodes have changed and include a list of which nodes have changed Arguments: Tree1 -- Pointer to the one of the device trees to compare Tree2 -- Pointer to the other device tree to be used in the comparison NumberDifferences -- Count of the number of differences found between the two device trees. NodePairList -- List of nodes that are different. The number of nodes in this list will be <= to NumberDifferences depending on whether space could be allocated for all the different nodes. The caller must call InitializeNodePairList() for this parameter before calling this function Return Value: TRUE if the device trees are the same FALSE if there are differences in the tree. NumberDifferences indicates how many nodes are different and NodePairList will contain some or all of the nodes that were different depending on memory constraints --*/ { *NumberDifferences = 0; CompareUSBDeviceNodes(Tree1, Tree2, NumberDifferences, NodePairList); return (0 == *NumberDifferences); } //***************************************************************************** // // InitializeNodePairList() // //***************************************************************************** VOID InitializeNodePairList( IN PNODE_PAIR_LIST NodePairList ) /*++ Routine Description: Initializes a node pair list that will be used in a call to CompareUSBDeviceTrees(); Arguments: NodePairList -- The list that needs to be initialized Return Value: NONE --*/ { NodePairList -> MaxNodePairs = 1; NodePairList -> CurrentNodePairs = 0; NodePairList -> NodePairs = ALLOC(NodePairList -> MaxNodePairs * sizeof(PUSB_DEVICE_NODE) * 2); if (NULL == NodePairList -> NodePairs) { NodePairList -> MaxNodePairs = 0; } return; } //***************************************************************************** // // DestroyNodePairList // //***************************************************************************** VOID DestroyNodePairList( IN PNODE_PAIR_LIST NodePairList ) /*++ Routine Description: Destroys a node pair list that was used in a call to CompareUSBDeviceTrees() Arguments: NodePairList -- The list that needs to be cleaned up Return Value: NONE --*/ { // // Free up the memory that had been allocated for the pairs if one // had been created // if (NULL != NodePairList -> NodePairs) { FREE(NodePairList -> NodePairs); } return; } //***************************************************************************** // // DestroyUSBDeviceTree() // //***************************************************************************** VOID __stdcall DestroyUSBDeviceTree( IN PUSB_DEVICE_TREE Tree ) /*++ Routine Description: Frees all the memory that was allocated in a call to BuildUSBDeviceTree(). This function used WalkUSBDeviceTree with the appropriate callbacks to perform the clean up. Arguments: Tree -- A pointer to the USB_DEVICE_TREE structure that needs to be cleaned up. Return Value: NONE --*/ { ULONG index; // // To destroy the whole tree, we just destroy the Tree node...This // could change in the future, hence the abstraction...The // WalkUSBDeviceTree() function is used with a post callback since we // need to clean up child nodes of the device before we can free // the memory allocated for a specific node // WalkUSBDeviceTree(Tree, NULL, DestroyUSBDeviceNode, NULL); return; } //***************************************************************************** // // WriteUSBDeviceTreeToFile() // //***************************************************************************** BOOLEAN __stdcall WriteUSBDeviceTreeToFile( IN HANDLE File, IN PUSB_DEVICE_TREE Tree ) /*++ Routine Description: Writes a given USB device tree to a file in order to save a given structure. This function uses a special binary format to save the relevant pieces of information for each device node. See the WriteUSBDeviceNodeToFile() function for more information on this format. Arguments: File -- A handle to the file used for saving the structure. This file handle must have been opened with GENERIC_WRITE access. Tree -- A pointer to the USB_DEVICE_TREE structure that needs to be saved to disk Return Value: TRUE if the whole tree was successfully written to the file FALSE if otherwise --*/ { BOOL success; // // Make use of the WalkUSBDeviceTree() function in order to write each // of the nodes to disk. A prewalk callback function is used since // the device file format contains the information for a given node and // then the information for all of its children. // success = WalkUSBDeviceTree(Tree, WriteUSBDeviceNodeToFile, NULL, (PVOID) File); return (success); } //***************************************************************************** // // ReadUSBDeviceTreeFromFile() // //***************************************************************************** BOOLEAN __stdcall ReadUSBDeviceTreeFromFile( IN HANDLE File, OUT PUSB_DEVICE_TREE *Tree ) /*++ Routine Description: Reads a given USB device tree from a file in order to reload a previous device tree. This function assumes the special binary format used by the WriteUSBDeviceTreeToFile() function. On reading the file, it creates a new USB_DEVICE_TREE structure to represent the information stored on the disk. Arguments: File -- A handle to the file used for reading the structure. This file handle must have been opened with GENERIC_READ access. Tree -- A pointer to the USB_DEVICE_TREE structure pointer that will point to the device tree created from the file. This structure should be destroyed with DestroyUSBDeviceTree when it is no longer needed. Return Value: TRUE if the whole tree was successfully read from the file FALSE if otherwise --*/ { *Tree = ReadUSBDeviceNodeFromFile(File); return (NULL != *Tree); } //***************************************************************************** // // WriteUSBDeviceTreeToTextFile() // //***************************************************************************** BOOLEAN __stdcall WriteUSBDeviceTreeToTextFile( IN HANDLE File, IN PUSB_DEVICE_TREE Tree ) /*++ Routine Description: Writes a given USB device tree to a file in text format as a tree structure. Arguments: File -- A handle to the text file used for saving the output. The file handle must have been opened with GENERIC_WRITE access. Tree -- A pointer to the USB_DEVICE_TREE structure that needs to be written to disk Return Value: TRUE if the whole tree was successfully written to the file FALSE if otherwise --*/ { NODE_TO_TEXT_CONTEXT context; BOOLEAN success; // // Initialize the context // context.File = File; context.Indentation = 0; // // Use the WalkUSBDeviceTree() function to display the tree. It uses // both a pre-walk callback and a post-walk callback. The pre-walk // callback outputs the line for the given node and updates the // indent size. The post-walk callback "undoes" the modifications // made to the indentation size. // success = WalkUSBDeviceTree(Tree, WriteUSBDeviceNodeToTextFilePre, WriteUSBDeviceNodeToTextFilePost, &context); return (success); } //***************************************************************************** // // WalkUSBDeviceTree // //***************************************************************************** BOOLEAN __stdcall WalkUSBDeviceTree( IN PUSB_DEVICE_TREE Tree, IN PUSB_DEVICE_NODE_CALLBACK PreWalkCallback OPTIONAL, IN PUSB_DEVICE_NODE_CALLBACK PostWalkCallback OPTIONAL, IN PVOID Context OPTIONAL ) /*++ Routine Description: Walks a given in a depth-first fashion. The caller can specify either a PreWalkCallback function, a PostWalkCallback function, or both. The PreWalkCallback function is called before the function visits each of the child nodes for a given node and the PostWalkCallback function will be called all the children of a node have been visited. The callback functions that are called should return TRUE or FALSE to indicate whether processing should continue further. If FALSE is returned, this function assumes an error occurred in the callback function and the callback function would like to terminate the walk. Arguments: Tree -- A pointer to the USB_DEVICE_TREE structure to be walked PreWalkCallback -- A pointer to a callback function that will be called before the child nodes of a node are walked. PostWalkCallback -- A pointer to a callback function that will be called after the child nodes of a node have been walked. Context -- A pointer to a context structure that will be passed to the callback functions. Return Value: TRUE if the whole tree was successfully walked FALSE if otherwise --*/ { BOOLEAN success; // // Simply call the corresponding node walk function... // success = WalkUSBDeviceNode(Tree, PreWalkCallback, PostWalkCallback, Context); return (success); } //***************************************************************************** // // CycleUSBPort // //***************************************************************************** BOOLEAN __stdcall CycleUSBPort( IN PUSB_DEVICE_NODE Node ) /*++ Routine Description: Cycles a user specified port. This function requires that USBTEST.SYS be loaded on the system. USBTEST.SYS is used to send the internal CYCLE_PORT ioctl that is only exposed at kernel mode. The caller of this function must pass in the node of the device which it would like to simulate a PnP on. This function will determine the parent hub and issue the request as appropriate for that hub. Arguments: Tree -- A pointer to the USB_DEVICE_NODE to be cycled PreWalkCallback -- A pointer to a callback function that will be called before the child nodes of a node are walked. PostWalkCallback -- A pointer to a callback function that will be called after the child nodes of a node have been walked. Context -- A pointer to a context structure that will be passed to the callback functions. Return Value: TRUE if the ioctl was successfully submitted FALSE if otherwise and GetLastError() contains the appropriate error code --*/ { PCYCLE_PORT_PARAMETERS cyclePortParams; PUSB_DEVICE_NODE parentHubNode; ULONG paramSize; BOOLEAN success; BOOLEAN isRootHub; ULONG nBytes; // // Check to see if we have a handle to the service. If not, we cannot // perform the test. // if (INVALID_HANDLE_VALUE == USBTestHandle) { SetLastError(ERROR_INVALID_HANDLE); return (FALSE); } // // Get the parent hub node // if (NULL == Node || NULL == Node -> Parent) { SetLastError(ERROR_INVALID_PARAMETER); return (FALSE); } parentHubNode = Node -> Parent; // // Cannot cycle a port if the parent is not actually a hub // if (Hub != parentHubNode -> NodeType) { SetLastError(ERROR_INVALID_PARAMETER); return (FALSE); } // // If this is a root hub, then we cannot cycle the port. This is due // to the fact that the USBD driver will not allow a create file for // the root hub port to succeed. // if (NULL == parentHubNode -> ConnectionInformation) { isRootHub = TRUE; } // // Calculate the amount of space needed for the cycle port // parameters buffer // paramSize = sizeof(CYCLE_PORT_PARAMETERS) + strlen(parentHubNode -> SymbolicLink) + 1; // // Allocate the space for the parameters // cyclePortParams = ALLOC(paramSize); if (NULL == cyclePortParams) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return (FALSE); } // // Set the relevant information in the parameters structure // cyclePortParams -> NodeIndex = Node -> ConnectionInformation -> ConnectionIndex; strcpy(&(cyclePortParams -> HubName[0]), parentHubNode -> SymbolicLink); // // Send this down to the USB test driver // success = DeviceIoControl(USBTestHandle, IOCTL_USBTEST_CYCLE_PORT, cyclePortParams, paramSize, NULL, 0, &nBytes, NULL); FREE(cyclePortParams); // // If the port failed to cycle due to an error cycling the root hub // then return status success so we don't confuse the calling routine. // if (!success && ERROR_NOT_SUPPORTED == GetLastError() && isRootHub) { success = TRUE; } return (success); } //***************************************************************************** // // StartUSBTest // //***************************************************************************** BOOLEAN __stdcall StartUSBTest( VOID ) /*++ Routine Description: This function attempts to start the USBTEST.SYS driver. If successful, it maintains a handle that can be used in future calls to communicate with the test driver. Since the loading mechanisms are different on Win9x and Win2k platforms, it detects the type of OS that is being run and then calls the appropriate loading function as implemented in service.c Arguments: NONE Return Value: TRUE if the driver was successfully loaded or was already loaded FALSE if otherwise and GetLastError() contains the appropriate error code --*/ { BOOL success; BOOL isWin9x; // // If USBTestHandle already contains a valid handle value then just // return TRUE as the service has already been started // if (INVALID_HANDLE_VALUE != USBTestHandle) { return (TRUE); } // // Call the appropriate driver loading routine based on whether this is // Win9x or Win2k // isWin9x = IsWindows9x(); if (isWin9x) { success = LoadWin9xWdmDriver("usbtest.sys"); } else { success = LoadWin2kWdmDriver(USBTEST_SERVICE_NAME, USBTEST_SERVICE_DESC, USBTEST_DRIVER_PATH); } // // If the driver was successfully loaded, get a handle to the driver // if (success) { USBTestHandle = CreateFile(USBTESTNAME, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == USBTestHandle) { success = FALSE; } } else { USBTestHandle = INVALID_HANDLE_VALUE; } return (success); } //***************************************************************************** // // StopUSBTest // //***************************************************************************** BOOLEAN __stdcall StopUSBTest( VOID ) /*++ Routine Description: This function attempts to stop the USBTEST.SYS driver if it has been loaded. Like loading, the unloading methods differ between Win9x and Win2k so the OS is detected and the appropriate function is called. Currently, the ability to statically unload a WDM on Win9x is not implemented. Arguments: NONE Return Value: TRUE if the driver was successfully loaded or was already loaded FALSE if otherwise and GetLastError() contains the appropriate error code --*/ { BOOL success; BOOL isWin9x; // // Close the open handle if we have it // if (INVALID_HANDLE_VALUE != USBTestHandle) { CloseHandle(USBTestHandle); USBTestHandle = INVALID_HANDLE_VALUE; } // // Unload the driver now...For Win2k, this means stopping the service // For Win9x, this cannot be done yet as it is not implemented in the // NTKern code // isWin9x = IsWindows9x(); if (isWin9x) { success = UnloadWin9xWdmDriver(); } else { success = UnloadWin2kWdmDriver(USBTEST_SERVICE_NAME); } return (success); } //***************************************************************************** // // StopUSBTest // //***************************************************************************** BOOLEAN __stdcall ParseReportDescriptor( IN PCHAR ReportDescriptor, IN ULONG DescriptorLength, OUT PHIDP_DEVICE_DESC *DeviceDesc ) /*++ Routine Description: This function uses the USBTEST.SYS driver to have the HID parser parse a given report descriptor. This is similar to what the hidview parser stress test does with USBDIAG but eliminates the need for the USBDIAG driver to be loaded. Arguments: ReportDescriptor -- A pointer a buffer that contains the descriptor that is to be parsed. DescriptorLength -- The length of the above report descriptor DeviceDesc -- A pointer to a HIDP_DEVICE_DESC pointer. This function allocates a structure to contain the data returned by the parser. The caller is responsible for freeing this structure when it is no longer needed. Return Value: TRUE if the descriptor was successfully parsed. FALSE if otherwise and GetLastError() contains the appropriate error code --*/ { PHIDP_COLLECTION_DESC hidCollectionDesc; PHIDP_DEVICE_DESC hidDeviceDesc; BOOL success; ULONG nBytes; PCHAR dataBuffer; ULONG dataBufferLength; DWORD errorCode; // // Initialize this variable since we'll set DeviceDesc to it on exit // hidDeviceDesc = NULL; // // Check that the USBTest service is started and open // if (INVALID_HANDLE_VALUE == USBTestHandle) { errorCode = ERROR_INVALID_HANDLE; success = FALSE; goto ParseReportDescriptor_Exit; } // // Start by allocating a buffer big enough to hold the report descriptor // dataBufferLength = DescriptorLength; success = FALSE; while (!success) { dataBuffer = ALLOC(dataBufferLength); if (NULL == dataBuffer) { errorCode = ERROR_OUTOFMEMORY; success = FALSE; break; } // // Copy the report descriptor into the allocated buffer // RtlCopyMemory(dataBuffer, ReportDescriptor, DescriptorLength); // // Call USBTest to parse the report descriptor // success = DeviceIoControl(USBTestHandle, IOCTL_USBTEST_PARSE, dataBuffer, DescriptorLength, dataBuffer, dataBufferLength, &nBytes, NULL); if (!success) { // // If the buffer was too small, the needed size is returned // in the buffer as a ULONG. Extract that value and // try the request again // errorCode = GetLastError(); dataBufferLength = *((PULONG) dataBuffer); FREE(dataBuffer); if (ERROR_MORE_DATA != errorCode) { break; } } } if (!success) { goto ParseReportDescriptor_Exit; } // // At this point, we have the device description data. However, // the pointers that are stored in the buffer are offsets into the // buffer. Let's repatch those for actual pointers // hidDeviceDesc = (PHIDP_DEVICE_DESC) dataBuffer; // // Resolve the collection data first // hidDeviceDesc -> CollectionDesc = (PHIDP_COLLECTION_DESC) (dataBuffer + ((ULONG) hidDeviceDesc -> CollectionDesc)); // // Next the preparsed data // hidCollectionDesc = hidDeviceDesc -> CollectionDesc; hidCollectionDesc -> PreparsedData = (PHIDP_PREPARSED_DATA) (dataBuffer + ((ULONG) hidCollectionDesc -> PreparsedData)); // // Now the report IDs // hidDeviceDesc -> ReportIDs = (PHIDP_REPORT_IDS) (dataBuffer + ((ULONG) hidDeviceDesc -> ReportIDs)); // // Indicate success for the error code and leave // errorCode = ERROR_SUCCESS; ParseReportDescriptor_Exit: *DeviceDesc = hidDeviceDesc; SetLastError(errorCode); return (success); }