2025-04-27 07:49:33 -04:00

2953 lines
74 KiB
C

/*++
Copyright (c) 1998-1998 Microsoft Corporation
Module Name:
DEVNODE.C
--*/
#include <windows.h>
#include <basetyps.h>
#include <cfgmgr32.h>
#include <wchar.h>
#include <string.h>
#include <winioctl.h>
#include <usbioctl.h>
#include "usbtest.h"
#include "local.h"
//*****************************************************************************
// D E F I N E S
//*****************************************************************************
#define INDENT_SIZE 4
#define DEFAULT_DEVICE_DESC ""
#define DEFAULT_DEVICE_NAME ""
#define DEFAULT_HUB_NAME ""
#define DEFAULT_CLASS_NAME "USB"
#define ROOT_HUB_DESC "RootHub"
#define USB_COMPOSITE_ID "USB\\COMPOSITE"
#define CalculateNodeVariableLength(nh) \
( (nh).NodeInfoExists*sizeof(USB_NODE_INFORMATION) + \
(nh).ConnectionInfoExists*sizeof(USB_NODE_CONNECTION_INFORMATION) + \
(nh).SymbolicLinkLength + \
(nh).DescriptionLength + \
(nh).ClassNameLength \
)
//*****************************************************************************
// T Y P E D E F S
//*****************************************************************************
#include <pshpack1.h>
typedef struct _FILE_NODE_HEADER
{
UCHAR NodeType;
UCHAR NumberOfChildren;
UCHAR NodeInfoExists;
UCHAR ConnectionInfoExists;
USHORT SymbolicLinkLength;
USHORT DescriptionLength;
USHORT ClassNameLength;
USHORT Reserved;
ULONG DeviceStatus;
ULONG DeviceProblem;
} FILE_NODE_HEADER, *PFILE_NODE_HEADER;
#include <poppack.h>
//*****************************************************************************
// G L O B A L S
//*****************************************************************************
CHAR buf[512]; // XXXXX How big does this have to be? Dynamically size it?
PCHAR ConnectionStatuses[] =
{
"NoDeviceConnected",
"DeviceConnected",
"DeviceFailedEnumeration",
"DeviceGeneralFailure",
"DeviceCausedOvercurrent",
"DeviceNotEnoughPower"
};
//*****************************************************************************
//
// DriverNameToDeviceInstance()
//
// Returns the config manager's device instance for the DevNode with
// the matching DriverName.
//
// Returns TRUE if the matching devnode is found..
// Returns FALSE if the matching devnode is not found and
// DeviceInstance will be set to 0.
//
//*****************************************************************************
BOOL
DriverNameToDeviceInstance(
IN PCHAR DriverName,
OUT DEVINST *DeviceInstance
)
{
DEVINST devInst;
DEVINST devInstNext;
CONFIGRET cr;
ULONG walkDone = 0;
ULONG len;
PCHAR devDesc;
//
// Initialize the DeviceInstance parameter to 0 so it is set
// appropriately on error
//
*DeviceInstance = 0;
//
// Get Root DevNode
//
cr = CM_Locate_DevNode(&devInst,
NULL,
0);
if (cr != CR_SUCCESS)
{
return (FALSE);
}
//
// Do a depth first search for the DevNode with a matching
// DriverName value
//
while (!walkDone)
{
//
// Get the DriverName value
//
len = sizeof(buf);
cr = CM_Get_DevNode_Registry_Property(devInst,
CM_DRP_DRIVER,
NULL,
buf,
&len,
0);
//
// If the DriverName value matches, return the DeviceDescription
//
if (cr == CR_SUCCESS && strcmp(DriverName, buf) == 0)
{
*DeviceInstance = devInst;
return (TRUE);
}
//
// This DevNode didn't match, go down a level to the first child.
//
cr = CM_Get_Child(&devInstNext,
devInst,
0);
if (cr == CR_SUCCESS)
{
devInst = devInstNext;
continue;
}
//
// Can't go down any further, go across to the next sibling. If
// there are no more siblings, go back up until there is a sibling.
// If we can't go up any further, we're back at the root and we're
// done.
//
for (;;)
{
cr = CM_Get_Sibling(&devInstNext,
devInst,
0);
if (cr == CR_SUCCESS)
{
devInst = devInstNext;
break;
}
cr = CM_Get_Parent(&devInstNext,
devInst,
0);
if (cr == CR_SUCCESS)
{
devInst = devInstNext;
}
else
{
walkDone = 1;
break;
}
}
}
return (FALSE);
}
//*****************************************************************************
//
// GetDeviceDesc()
//
// Returns the Device Description for this particular device instance
// Returns NULL if the device description could not be obtained.
//
// The caller is responsible for freeing the return value when it is
// no longer needed.
//
//*****************************************************************************
PCHAR
GetDeviceDesc(
DEVINST DeviceInstance
)
{
CONFIGRET cr;
ULONG len;
PCHAR devDesc;
devDesc = NULL;
if (0 != DeviceInstance)
{
len = sizeof(buf);
cr = CM_Get_DevNode_Registry_Property(DeviceInstance,
CM_DRP_DEVICEDESC,
NULL,
buf,
&len,
0);
if (cr == CR_SUCCESS)
{
devDesc = ALLOC(strlen(buf)+1);
if (NULL != devDesc)
{
strcpy(devDesc, buf);
}
}
}
if (NULL == devDesc)
{
devDesc = ALLOC(sizeof(DEFAULT_DEVICE_DESC));
if (NULL != devDesc)
{
strcpy(devDesc, DEFAULT_DEVICE_DESC);
}
}
return (devDesc);
}
//*****************************************************************************
//
// GetSymbolicLink
//
// Returns the SymbolicLink of the device instance
// Returns NULL if the the symbolic link could not be retrieved
//
// The caller is responsible for freeing the return value when it is
// no longer needed.
//
//*****************************************************************************
PCHAR
GetSymbolicLink(
DEVINST DeviceInstance
)
{
HKEY instKey;
HKEY paramKey;
CONFIGRET cr;
ULONG len;
PCHAR symLink;
LONG result;
symLink = NULL;
if (0 != DeviceInstance)
{
cr = CM_Open_DevNode_Key(DeviceInstance,
KEY_QUERY_VALUE,
0,
RegDisposition_OpenExisting,
&instKey,
0);
if (CR_SUCCESS == cr)
{
//
// If we successfully got a handle to the instance key, then
// get a handle to the device parameters sub key
//
len = sizeof(buf);
result = RegQueryValueEx(instKey,
"SymbolicName",
NULL,
NULL,
buf,
&len);
//
// Don't need the instance key anymore...
//
RegCloseKey(instKey);
if (ERROR_SUCCESS == result)
{
symLink = ALLOC(strlen(buf) - strlen("\\??\\") + 1);
if (NULL != symLink)
{
strcpy(symLink, buf+strlen("\\??\\"));
}
}
}
}
if (NULL == symLink)
{
symLink = ALLOC(sizeof(DEFAULT_DEVICE_NAME));
if (NULL != symLink)
{
strcpy(symLink, DEFAULT_DEVICE_NAME);
}
}
return (symLink);
}
//*****************************************************************************
//
// GetDeviceClassName
//
// Returns the Class Name of the device instance
// Returns NULL if the the class name could not be retrieved
//
// The caller is responsible for freeing the return value when it is
// no longer needed.
//
//*****************************************************************************
PCHAR
GetDeviceClassName(
DEVINST DeviceInstance
)
{
CONFIGRET cr;
ULONG len;
PCHAR className;
className = NULL;
if (0 != DeviceInstance)
{
len = sizeof(buf);
cr = CM_Get_DevNode_Registry_Property(DeviceInstance,
CM_DRP_CLASS,
NULL,
buf,
&len,
0);
if (cr == CR_SUCCESS)
{
className = ALLOC(strlen(buf)+1);
if (NULL != className)
{
strcpy(className, buf);
}
}
}
if (NULL == className)
{
className = ALLOC(sizeof(DEFAULT_CLASS_NAME));
if (NULL != className)
{
strcpy(className, DEFAULT_CLASS_NAME);
}
}
return (className);
}
//*****************************************************************************
//
// GetDeviceState
//
// Returns the state for a given device instance which includes the
// config manager's device status and device problem code
//
// Returns FALSE if an error occured and device state/problem will be zero
//
//*****************************************************************************
BOOL
GetDeviceState(
IN DEVINST DeviceInstance,
OUT PULONG DeviceStatus,
OUT PULONG DeviceProblem
)
{
CONFIGRET cr;
//
// Validate the parameters to the call
//
if (NULL == DeviceStatus || NULL == DeviceProblem || 0 == DeviceInstance)
{
return (FALSE);
}
//
// Call config man to get the device status and device problem. Expecting
// CR_SUCCESS on return. If anything else, return FALSE and set the
// DeviceStatus and DeviceProblem fields to 0.
//
cr = CM_Get_DevNode_Status(DeviceStatus,
DeviceProblem,
DeviceInstance,
0);
if (CR_SUCCESS != cr)
{
*DeviceStatus = 0;
*DeviceProblem = 0;
}
return (CR_SUCCESS == cr);
}
//*****************************************************************************
//
// GetDeviceInfo
//
// Takes a device instance and a device node as input and sets all the fields
// of the device node that are retrieved from the registry via the device
// instance. If there is an error trying to get a certain piece of
// (ie. it does't exist), a default is used. The only time a return
// code of FALSE will be returned is when a memory allocation failed.
// In this manor, the tree can be established with some sort of default
// values, but we won't have to worry about having NULL pointers within
// the tree.
//
//*****************************************************************************
BOOL
GetDeviceInfo(
IN DEVINST DeviceInstance,
IN BOOL IsHub,
IN OUT PUSB_DEVICE_NODE DeviceNode
)
{
PCHAR className;
PCHAR deviceDesc;
PCHAR symbolicLink;
ULONG statusCode;
ULONG problemCode;
BOOL success;
//
// Initialize for later checks
//
className = NULL;
deviceDesc = NULL;
symbolicLink = NULL;
statusCode = 0;
problemCode = 0;
success = TRUE;
//
// Try to get the string information initially based
// on the device instance. If any of these are NULL, then it
// either didn't exist or there was a memory allocation failure.
// Either way, we'll at least try to create default names.
//
if (NULL == DeviceNode -> ClassName)
{
className = GetDeviceClassName(DeviceInstance);
success = (NULL != className);
}
if (NULL == DeviceNode -> Description)
{
deviceDesc = GetDeviceDesc(DeviceInstance);
success = (success && (NULL != deviceDesc));
}
if (NULL == DeviceNode -> SymbolicLink)
{
symbolicLink = GetSymbolicLink(DeviceInstance);
success = (success && (NULL != symbolicLink));
}
//
// Get the device state. Success will be ignored since default
// values of 0 are returned for status and problem codes if there
// was an error. That's good enough for here.
//
GetDeviceState(DeviceInstance,
&statusCode,
&problemCode);
//
// If no device instance was specified or an error occurred trying to get
// one of the strings, then the default value should be attempted to be
// used.
//
if (!success)
{
if (NULL != className)
{
FREE(className);
}
if (NULL != deviceDesc)
{
FREE(deviceDesc);
}
if (NULL != symbolicLink)
{
FREE(symbolicLink);
}
}
else
{
if (NULL == DeviceNode -> ClassName)
{
DeviceNode -> ClassName = className;
}
if (NULL == DeviceNode -> Description)
{
DeviceNode -> Description = deviceDesc;
}
if (NULL == DeviceNode -> SymbolicLink)
{
DeviceNode -> SymbolicLink = symbolicLink;
}
DeviceNode -> DeviceStatus = statusCode;
DeviceNode -> DeviceProblem = problemCode;
}
return (success);
}
//*****************************************************************************
//
// WideStrToMultiStr()
//
//*****************************************************************************
PCHAR
WideStrToMultiStr(
PWCHAR WideStr
)
{
ULONG nBytes;
PCHAR MultiStr;
// Get the length of the converted string
//
nBytes = WideCharToMultiByte(
CP_ACP,
0,
WideStr,
-1,
NULL,
0,
NULL,
NULL);
if (nBytes == 0)
{
return NULL;
}
// Allocate space to hold the converted string
//
MultiStr = ALLOC(nBytes);
if (MultiStr == NULL)
{
return NULL;
}
// Convert the string
//
nBytes = WideCharToMultiByte(
CP_ACP,
0,
WideStr,
-1,
MultiStr,
nBytes,
NULL,
NULL);
if (nBytes == 0)
{
FREE(MultiStr);
return NULL;
}
return MultiStr;
}
DEVINST
GetHCDDeviceInstance(
IN HANDLE HCHandle
)
{
USB_HCD_DRIVERKEY_NAME driverKeyName;
PUSB_HCD_DRIVERKEY_NAME driverKeyNameW;
PCHAR driverKeyNameA;
BOOL success;
ULONG nBytes;
DEVINST deviceInstance;
//
// Initialize the allocated memory structure first
//
driverKeyNameW = NULL;
driverKeyNameA = NULL;
deviceInstance = 0;
//
// Get the number of bytes needed for the driver key name
//
success = DeviceIoControl(HCHandle,
IOCTL_GET_HCD_DRIVERKEY_NAME,
&driverKeyName,
sizeof(driverKeyName),
&driverKeyName,
sizeof(driverKeyName),
&nBytes,
NULL);
if (!success)
{
goto GetHCDDeviceInstance_Exit;
}
//
// Allocate space to hold the driver key name
//
nBytes = driverKeyName.ActualLength;
if (nBytes <= sizeof(driverKeyName))
{
goto GetHCDDeviceInstance_Exit;
}
//
// Allocate the correct amount of space for the driver key name
//
driverKeyNameW = ALLOC(nBytes);
if (NULL == driverKeyNameW)
{
goto GetHCDDeviceInstance_Exit;
}
//
// Get the name of the driver key of the host controller
//
success = DeviceIoControl(HCHandle,
IOCTL_GET_HCD_DRIVERKEY_NAME,
driverKeyNameW,
nBytes,
driverKeyNameW,
nBytes,
&nBytes,
NULL);
if (!success)
{
goto GetHCDDeviceInstance_Exit;
}
//
// Convert the name to ASCII
//
driverKeyNameA = WideStrToMultiStr(driverKeyNameW -> DriverKeyName);
if (NULL == driverKeyNameA)
{
goto GetHCDDeviceInstance_Exit;
}
success = DriverNameToDeviceInstance(driverKeyNameA, &deviceInstance);
GetHCDDeviceInstance_Exit:
//
// Free up the driver key names that were allocated
//
if (NULL != driverKeyNameW)
{
FREE(driverKeyNameW);
}
if (NULL != driverKeyNameA)
{
FREE(driverKeyNameA);
}
return (deviceInstance);
}
//*****************************************************************************
//
// IsCompositeDevice()
//
//*****************************************************************************
BOOL
IsCompositeDevice(
IN DEVINST DeviceInstance,
OUT PBOOL IsComposite,
OUT PULONG NumberChildren
)
{
CONFIGRET cr;
DEVINST child;
DEVINST nextChild;
PTCHAR property;
PTCHAR walk;
ULONG len;
//
// Get the compatible IDs for this device...We will want to look
// for USB\COMPOSITE in this list.
//
//
// Retrieve the compatible ids
//
len = 0;
cr = CM_Get_DevNode_Registry_Property(DeviceInstance,
CM_DRP_COMPATIBLEIDS,
NULL,
NULL,
&len,
0);
if (CR_BUFFER_SMALL != cr)
{
return (FALSE);
}
property = ALLOC(len);
if (NULL == property)
{
return (FALSE);
}
cr = CM_Get_DevNode_Registry_Property(DeviceInstance,
CM_DRP_COMPATIBLEIDS,
NULL,
property,
&len,
0);
if (CR_SUCCESS != cr)
{
FREE(property);
return (FALSE);
}
//
// Successfully got the compatible ids, now search them
//
*IsComposite = FALSE;
for (walk = property; *walk != '\0'; walk += (lstrlen(walk)+1))
{
if (0 == _stricmp(walk, USB_COMPOSITE_ID))
{
*IsComposite = TRUE;
break;
}
}
FREE(property);
//
// If not a composite, just return with the number of children = 0
//
if (!*IsComposite)
{
*NumberChildren = 0;
return (TRUE);
}
//
// If it is a composite device, count the number of child nodes
//
cr = CM_Get_Child(&child,
DeviceInstance,
0);
//
// This is bad, there should be at least one child
//
if (CR_SUCCESS != cr)
{
return (FALSE);
}
//
// OK, there are child nodes for this device which means it's a composite
// Find out how many child nodes there are...This should be the number
// of interfaces that were enumerated by the parent device.
//
*NumberChildren = 1;
while (1)
{
cr = CM_Get_Sibling(&nextChild, child, 0);
//
// Once we get this return code, we've determined the number of
// children
//
if (CR_NO_SUCH_DEVNODE == cr)
{
return (TRUE);
}
if (CR_SUCCESS == cr)
{
(*NumberChildren)++;
child = nextChild;
}
else
{
//
// This is some error that we are not expecting...
// return FALSE again.
//
return (FALSE);
}
}
return (FALSE);
}
//*****************************************************************************
//
// CreateUSBDeviceNode()
//
//*****************************************************************************
PUSB_DEVICE_NODE
CreateUSBDeviceNode(
IN USB_DEVICE_NODE_TYPE NodeType,
IN ULONG NumberOfChildren
)
{
PUSB_DEVICE_NODE newNode;
ULONG index;
newNode = ALLOC(sizeof(USB_DEVICE_NODE) +
NumberOfChildren * sizeof(PUSB_DEVICE_NODE));
if (NULL != newNode)
{
newNode -> NodeType = NodeType;
newNode -> SymbolicLink = NULL;
newNode -> Description = NULL;
newNode -> Parent = NULL;
newNode -> NodeInformation = NULL;
newNode -> ConnectionInformation = NULL;
newNode -> DeviceInstance = 0;
newNode -> DeviceStatus = 0;
newNode -> DeviceProblem = 0;
newNode -> HCHandle = INVALID_HANDLE_VALUE;
newNode -> NumberOfChildren = NumberOfChildren;
for (index = 0; index < NumberOfChildren; index++)
{
newNode -> Children[index] = NULL;
}
}
return (newNode);
}
//*****************************************************************************
//
// DestroyUSBDeviceNode()
//
//*****************************************************************************
BOOLEAN
DestroyUSBDeviceNode(
IN PUSB_DEVICE_NODE Node,
IN PVOID Context
)
{
//
// Do cleanup work for the node...
//
if (NULL != Node -> SymbolicLink)
{
FREE(Node -> SymbolicLink);
}
if (NULL != Node -> Description)
{
FREE(Node -> Description);
}
if (NULL != Node -> ClassName)
{
FREE(Node -> ClassName);
}
if (NULL != Node -> NodeInformation)
{
FREE(Node -> NodeInformation);
}
//
// Special case for Interface nodes since they share the connection
// information with the parent so we don't free it.
//
if (NULL != Node -> ConnectionInformation && Node -> NodeType != MIInterface)
{
FREE(Node -> ConnectionInformation);
}
if (INVALID_HANDLE_VALUE != Node -> HCHandle)
{
CloseHandle(Node -> HCHandle);
}
FREE(Node);
return (TRUE);
}
//*****************************************************************************
//
// WriteUSBDeviceNodeToTextFilePre()
//
//*****************************************************************************
BOOLEAN
WriteUSBDeviceNodeToTextFilePre(
PUSB_DEVICE_NODE Node,
PNODE_TO_TEXT_CONTEXT Context
)
{
PCHAR displayString;
DWORD nBytes;
BOOL success;
ULONG indent;
//
// Initialize displayString to point to the beginning of the buffer
//
displayString = &(buf[0]);
//
// Indent the string first
//
indent = Context -> Indentation;
if (indent > 0)
{
FillMemory(displayString, indent, ' ');
}
displayString = &buf[indent];
*displayString = '\0';
//
// Check for an error code on the node. If there is a problem, indicate
// an error in the display string.
//
//
// Add the node connection information if the node is not a composite
// interface. If a composite interface, this information has already
// been displayed for the parent.
//
if (Node -> ConnectionInformation && MIInterface != Node -> NodeType)
{
CHAR str[32];
wsprintf(str,
"[Port%d] ",
Node -> ConnectionInformation -> ConnectionIndex);
strcat(displayString, str);
strcat(displayString,
ConnectionStatuses[Node -> ConnectionInformation -> ConnectionStatus]);
strcat(displayString, ": ");
}
else if (MIInterface == Node -> NodeType)
{
//
// If it's an interface on a composite device, display some interface
// information.
//
strcat(displayString, "[Interface]: ");
}
if (0 != Node -> DeviceProblem)
{
CHAR str[10];
wsprintf(str, "(!:%d) ", Node -> DeviceProblem);
strcat(displayString, str);
}
if (NULL != Node -> Description)
{
strcat(displayString, Node -> Description);
strcat(displayString, " ");
}
if (Node -> ConnectionInformation &&
Node -> ConnectionInformation -> ConnectionStatus == DeviceConnected)
{
CHAR str[32];
wsprintf(str,
"(VID: %04X, PID: %04X) ",
Node -> ConnectionInformation -> DeviceDescriptor.idVendor,
Node -> ConnectionInformation -> DeviceDescriptor.idProduct);
strcat(displayString, str);
if (Node -> ClassName)
{
strcat(displayString, "(Class: ");
strcat(displayString, Node -> ClassName);
strcat(displayString, ")");
}
}
if (Node -> NodeType == MIParent)
{
CHAR str[40];
wsprintf(str,
" (Composite: %d interfaces)",
Node -> NumberOfChildren);
strcat (displayString, str);
}
strcat(displayString, "\r\n");
success = WriteFile(Context -> File,
buf,
strlen(buf),
&nBytes,
NULL);
//
// Increment the indentation for the children
//
Context -> Indentation += INDENT_SIZE;
return (success);
}
//*****************************************************************************
//
// WriteUSBDeviceNodeToTextFilePost()
//
//*****************************************************************************
BOOLEAN
WriteUSBDeviceNodeToTextFilePost(
PUSB_DEVICE_NODE Node,
PNODE_TO_TEXT_CONTEXT Context
)
{
Context -> Indentation -= INDENT_SIZE;
return (TRUE);
}
//*****************************************************************************
//
// WalkUSBDeviceNode
//
//*****************************************************************************
BOOLEAN
WalkUSBDeviceNode(
IN PUSB_DEVICE_NODE Node,
IN PUSB_DEVICE_NODE_CALLBACK PreWalkCallback,
IN PUSB_DEVICE_NODE_CALLBACK PostWalkCallback,
IN PVOID Context
)
{
BOOLEAN continueWalk;
ULONG index;
//
// In walking with a USB device node, the following steps are going to
// be performed:
//
// 1) Calling the PreWalkCallback if it exists and getting continuation
// status to determine if walk should proceed.
//
// 2) If walk proceeds, recursively call WalkUSBDeviceNode() on all
// child nodes if continuation status is positive.
//
// 3) Call the PostWalkCallback function if we are to proceed further
// and once again get continuation status.
//
// 4) Return the continuation status
//
//
// Step 1
//
continueWalk = TRUE;
if (NULL != PreWalkCallback)
{
continueWalk = (*PreWalkCallback)(Node, Context);
}
//
// Step 2
//
for (index = 0; index < Node -> NumberOfChildren && continueWalk; index++)
{
continueWalk = WalkUSBDeviceNode(Node -> Children[index],
PreWalkCallback,
PostWalkCallback,
Context);
}
//
// Step 3
//
if (continueWalk && NULL != PostWalkCallback)
{
continueWalk = (*PostWalkCallback)(Node, Context);
}
//
// Step 4
//
return (continueWalk);
}
BOOL
EnumerateHostController(
IN PUSB_DEVICE_NODE HCNode
)
{
PUSB_DEVICE_NODE rootHubNode;
PCHAR rootHubName;
DEVINST rootHubInstance;
CONFIGRET cr;
//
// Get the device instance for the root hub. It is assumed that this
// is the first child of the host controller instance.
//
cr = CM_Get_Child(&rootHubInstance,
HCNode -> DeviceInstance,
0);
if (CR_SUCCESS != cr)
{
rootHubInstance = 0;
}
//
// Get the root hub name
//
rootHubName = GetRootHubName(HCNode -> HCHandle);
if (NULL == rootHubName)
{
return (FALSE);
}
//
// Enumerate the hub and in the process creating a root hub node if
// successful...At this point, just mark the device instance, device
// status, and device problem fields as 0. This may not be exactly what
// we want to do in the future.
//
rootHubNode = EnumerateHub(rootHubInstance, rootHubName, NULL);
//
// If not successful, free the rootHubName and return FALSE
//
if (NULL == rootHubNode)
{
FREE(rootHubName);
goto EnumerateHostController_Exit;
}
//
// Otherwise, add the new node to the tree
//
rootHubNode -> Parent = HCNode;
HCNode -> Children[0] = rootHubNode;
HCNode -> NumberOfChildren = 1;
EnumerateHostController_Exit:
return (NULL != rootHubNode);
}
PUSB_DEVICE_NODE
EnumerateNonHub(
DEVINST DeviceInstance,
PUSB_NODE_CONNECTION_INFORMATION ConnectionInfo
)
{
PUSB_DEVICE_NODE deviceNode;
BOOL success;
//
// Allocate a device node
//
deviceNode = CreateUSBDeviceNode(Device, 0);
if (NULL == deviceNode)
{
goto EnumerateNonHub_Exit;
}
//
// Change the default settings for a USB Device node to contain the
// necessary device info
//
deviceNode -> ConnectionInformation = ConnectionInfo;
deviceNode -> DeviceInstance = DeviceInstance;
success = GetDeviceInfo(DeviceInstance, FALSE, deviceNode);
if (!success)
{
DestroyUSBDeviceNode(deviceNode, NULL);
deviceNode = NULL;
}
EnumerateNonHub_Exit:
return (deviceNode);
}
PUSB_DEVICE_NODE
EnumerateCompositeDevice(
DEVINST DeviceInstance,
PUSB_NODE_CONNECTION_INFORMATION ConnectionInfo,
ULONG NumberInterfaces
)
{
PUSB_DEVICE_NODE compositeNode;
PUSB_DEVICE_NODE deviceNode;
DEVINST childInstance;
DEVINST nextChild;
ULONG index;
BOOL success;
CONFIGRET cr;
ULONG nBytes;
//
// Initialize the memory pointer variables to NULL to properly
// cleanup on exit
//
success = FALSE;
//
// Allocate a device node
//
compositeNode = CreateUSBDeviceNode(MIParent, NumberInterfaces);
if (NULL == compositeNode)
{
goto EnumerateCompositeDevice_Exit;
}
//
// Initialize the number of children for the deviceNode to zero
// since this is the running count of successfully enumerated
// ports. If an error occurs somewhere in this function, we'll
// need this information to cleanup the previously allocated nodes
//
compositeNode -> NumberOfChildren = 0;
compositeNode -> ConnectionInformation = ConnectionInfo;
compositeNode -> DeviceInstance = DeviceInstance;
success = GetDeviceInfo(DeviceInstance, FALSE, compositeNode);
if (!success)
{
DestroyUSBDeviceNode(compositeNode, NULL);
compositeNode = NULL;
goto EnumerateCompositeDevice_Exit;
}
//
// Start enumerating the interfaces...Begin by getting the first child
// For each child, we need to get device name, class name, and device
// description. Then get the next sibling..
//
cr = CM_Get_Child(&childInstance, DeviceInstance, 0);
for (index = 1; index <= NumberInterfaces && (CR_SUCCESS == cr); index++)
{
//
// If the device instance is valid, try to get the device
// description, the class name for the interface, along with
// a device name for the interface.
//
deviceNode = CreateUSBDeviceNode(MIInterface, 0);
//
// If we couldn't create a device node, bail out as an error
//
if (NULL == deviceNode)
{
success = FALSE;
break;
}
//
// Add the device node to the tree
//
deviceNode -> Parent = compositeNode;
deviceNode -> ConnectionInformation = ConnectionInfo;
deviceNode -> DeviceInstance = childInstance;
success = GetDeviceInfo(childInstance, FALSE, deviceNode);
if (!success)
{
break;
}
//
// Store the new interface node in the composite
// node's list of children
//
compositeNode -> Children[compositeNode -> NumberOfChildren] = deviceNode;
compositeNode -> NumberOfChildren++;
//
// Get the next sibling
//
cr = CM_Get_Sibling(&childInstance, childInstance, 0);
}
//
// If the case is true, then we succesfully got the information for
// all interfaces, so set success appropriately.
//
if (CR_NO_SUCH_DEVNODE == cr && index > NumberInterfaces)
{
success = TRUE;
}
EnumerateCompositeDevice_Exit:
//
// If there was an error, cycle through all the device nodes that were
// successfully created during this enumeration process and clean them
// up.
//
if (!success && NULL != compositeNode)
{
for (index = 0; index < compositeNode -> NumberOfChildren; index++)
{
DestroyUSBDeviceNode(compositeNode -> Children[index], NULL);
}
compositeNode -> NumberOfChildren = 0;
DestroyUSBDeviceNode(compositeNode, NULL);
compositeNode = NULL;
}
return (compositeNode);
}
PUSB_DEVICE_NODE
EnumerateHub(
DEVINST DeviceInstance,
PCHAR HubName,
PUSB_NODE_CONNECTION_INFORMATION ConnectionInfo
)
{
PUSB_NODE_INFORMATION hubNodeInfo;
PUSB_DEVICE_NODE hubNode;
HANDLE hubHandle;
PCHAR deviceName;
ULONG nBytes;
ULONG nPorts;
BOOL success;
//
// Initialize the handle value since it may need to be closed on
// exiting the function. Also initialize the hubNode to NULL since
// this is what is returned by the function and NULL will indicate an
// error.
//
hubHandle = INVALID_HANDLE_VALUE;
hubNode = NULL;
//
// Create the full device name for the hub name passed in...
//
deviceName = (PCHAR) ALLOC(strlen(HubName) + sizeof("\\\\.\\"));
if (NULL == deviceName)
{
goto EnumerateHub_Exit;
}
wsprintf(deviceName, "\\\\.\\%s", HubName);
//
// Open a handle to the hub device
//
hubHandle = CreateFile(deviceName,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
//
// Free the device name, it is no longer needed
//
FREE(deviceName);
if (INVALID_HANDLE_VALUE == hubHandle)
{
goto EnumerateHub_Exit;
}
//
// Allocate space for the node information
//
hubNodeInfo = ALLOC(sizeof(USB_NODE_INFORMATION));
if (NULL == hubNodeInfo)
{
goto EnumerateHub_Exit;
}
//
// Query the hub for the node information
//
success = DeviceIoControl(hubHandle,
IOCTL_USB_GET_NODE_INFORMATION,
hubNodeInfo,
sizeof(USB_NODE_INFORMATION),
hubNodeInfo,
sizeof(USB_NODE_INFORMATION),
&nBytes,
NULL);
if (!success)
{
FREE(hubNodeInfo);
goto EnumerateHub_Exit;
}
//
// If we got this far, we now have the hub node information. This tells
// us how many ports we have on this hub allowing for the creation of
// a new device node to represent the hub in the tree.
//
nPorts = hubNodeInfo -> u.HubInformation.HubDescriptor.bNumberOfPorts;
hubNode = CreateUSBDeviceNode(Hub, nPorts);
if (NULL == hubNode)
{
success = FALSE;
FREE(hubNodeInfo);
goto EnumerateHub_Exit;
}
//
// Enumerate each of the ports
//
success = EnumerateHubPorts(hubNode, hubHandle, nPorts);
if (!success)
{
FREE(hubNodeInfo);
goto EnumerateHub_Exit;
}
//
// Change the default settings for a USB Device node to contain the
// necessary hub info
//
hubNode -> SymbolicLink = HubName;
hubNode -> NodeInformation = hubNodeInfo;
hubNode -> ConnectionInformation = ConnectionInfo;
hubNode -> DeviceInstance = DeviceInstance;
success = GetDeviceInfo(DeviceInstance, TRUE, hubNode);
EnumerateHub_Exit:
if (!success)
{
if (NULL != hubNode)
{
DestroyUSBDeviceNode(hubNode, NULL);
hubNode = NULL;
}
}
if (INVALID_HANDLE_VALUE != hubHandle)
{
CloseHandle(hubHandle);
}
return (hubNode);
}
BOOL
EnumerateHubPorts(
IN PUSB_DEVICE_NODE HubNode,
IN HANDLE HubHandle,
IN ULONG NumberOfPorts
)
{
PUSB_NODE_CONNECTION_INFORMATION connectInfo;
PUSB_DEVICE_NODE deviceNode;
DEVINST deviceInstance;
ULONG index;
BOOL success;
BOOL isCompositeDevice;
PCHAR driverKeyName;
PCHAR hubName;
ULONG nBytes;
ULONG numChildInterfaces;
CONFIGRET cr;
ULONG deviceStatus;
ULONG deviceProblem;
//
// Initialize the memory pointer variables to NULL to properly
// cleanup on exit
//
driverKeyName = NULL;
hubName = NULL;
connectInfo = NULL;
//
// Initialize the number of children for the hubNode to zero
// since this is the running count of successfully enumerated
// ports. If an error occurs somewhere in this function, we'll
// need this information to cleanup the previously allocated nodes
//
HubNode -> NumberOfChildren = 0;
//
// Start enumerating the ports...The connection info used by the hub
// driver references the hub ports using 1 based indexing instead of
// 0 based
//
for (index = 1; index <= NumberOfPorts; index++)
{
//
// Allocate space for the connection information...We don't care about
// the open pipes so just allocate the default size
//
connectInfo = ALLOC(sizeof(USB_NODE_CONNECTION_INFORMATION));
//
// If there's a failure, indicate it as such
//
if (NULL == connectInfo)
{
success = FALSE;
break;
}
//
// Setup the connection info for the current port being queried
//
connectInfo -> ConnectionIndex = index;
//
// Query the hub for the connection info
//
success = DeviceIoControl(HubHandle,
IOCTL_USB_GET_NODE_CONNECTION_INFORMATION,
connectInfo,
sizeof(USB_NODE_CONNECTION_INFORMATION),
connectInfo,
sizeof(USB_NODE_CONNECTION_INFORMATION),
&nBytes,
NULL);
if (!success)
{
break;
}
//
// If no device is connected, just create a default port
// node with just the connection status
//
if (NoDeviceConnected == connectInfo -> ConnectionStatus)
{
deviceNode = CreateUSBDeviceNode(EmptyPort, 0);
if (NULL == deviceNode)
{
success = FALSE;
break;
}
if (!GetDeviceInfo(0, FALSE, deviceNode))
{
DestroyUSBDeviceNode(deviceNode, NULL);
success = FALSE;
break;
}
deviceNode -> ConnectionInformation = connectInfo;
}
else
{
//
// Otherwise, a device is connected, get the device instance
//
driverKeyName = GetDriverKeyName(HubHandle, index);
//
// Get a handle to the device instance that could be used in
// future calls for registry information
//
if (NULL != driverKeyName)
{
success = DriverNameToDeviceInstance(driverKeyName,
&deviceInstance);
}
else
{
deviceInstance = 0;
}
//
// If the device instance is valid, determine if this
// device is a composite device.
//
isCompositeDevice = FALSE;
if (0 != deviceInstance)
{
//
// Get the symbolic link (HubName) for the hub here since
// we need the handle to the hub and the index. All other
// symlinks will be retrieved when the node is created
// in the specific node enumeration code. This isn't the
// most elegant way but it works.
//
if (connectInfo -> DeviceIsHub)
{
hubName = GetExternalHubName(HubHandle, index);
if (NULL == hubName)
{
hubName = ALLOC(sizeof(DEFAULT_HUB_NAME));
if (NULL == hubName)
{
success = FALSE;
FREE(driverKeyName);
break;
}
strcpy(hubName, DEFAULT_HUB_NAME);
}
}
else
{
//
// If not a hub, need to determine if this is a composite
// device or not.
//
success = IsCompositeDevice(deviceInstance,
&isCompositeDevice,
&numChildInterfaces);
//
// If an error occurred above, then at this point
// we want to just mark this as a non-composite device
//
if (!success)
{
isCompositeDevice = FALSE;
}
}
}
if (NULL != driverKeyName)
{
FREE(driverKeyName);
}
//
// If it is a hub, enumerate it as such.
// Otherwise, enumerate as a non-hub device
//
if (connectInfo -> DeviceIsHub)
{
//
// Enumerate the hub...
//
deviceNode = EnumerateHub(deviceInstance,
hubName,
connectInfo);
}
else if (isCompositeDevice)
{
//
// If a composite device, call the specific enumeration
// routine.
//
deviceNode = EnumerateCompositeDevice(deviceInstance,
connectInfo,
numChildInterfaces);
}
else
{
//
// Not a hub or composite device, enumerate as such
//
deviceNode = EnumerateNonHub(deviceInstance, connectInfo);
}
}
//
// If we couldn't create a device node, bail out as an error
//
if (NULL == deviceNode)
{
success = FALSE;
break;
}
//
// Add the device node to the tree
//
deviceNode -> Parent = HubNode;
HubNode -> Children[HubNode -> NumberOfChildren] = deviceNode;
HubNode -> NumberOfChildren++;
//
// If we get to this point, then the device node was properly set
// up and we stored these three pointers into the device node which
// means we don't want to free them on exit.
//
hubName = NULL;
connectInfo = NULL;
}
//
// Cleanup any previously allocated structures that may need to be freed
//
if (NULL != connectInfo)
{
FREE(connectInfo);
}
if (NULL != hubName)
{
FREE(hubName);
}
//
// If there was an error, cycle through all the device nodes that were
// successfully created during this enumeration process and clean them
// up.
//
if (!success)
{
for (index = 0; index < HubNode -> NumberOfChildren; index++)
{
DestroyUSBDeviceNode(HubNode -> Children[index], NULL);
}
HubNode -> NumberOfChildren = 0;
}
return (success);
}
//*****************************************************************************
//
// WriteUSBDeviceNodeToFile()
//
//*****************************************************************************
BOOLEAN
WriteUSBDeviceNodeToFile(
IN PUSB_DEVICE_NODE Node,
IN HANDLE File
)
{
//
// To write a node to the file, we will write a node completely to the
// disk and then write each of the children to disk. The information
// for each node data packet that is written to disk contains two parts,
// the fixed length node header and the variable length data. Each
// part is laid out as follows:
//
//
// Node Header
// -----------
// NodeType
// NumberOfChildren
// NodeInformationExists
// ConnectionInformationExists
// SymbolicLinkLength
// DescriptionLength
// ClassNameLength
// DeviceStatus
// DeviceProblem
//
// Variable Data
// -------------
// SymbolicLink
// Description
// ClassName
// NodeInformation
// ConnectionInformation
//
FILE_NODE_HEADER nodeHeader;
PCHAR dataBuffer;
PCHAR bufferWalk;
BOOL success;
ULONG bufferLength;
ULONG variableLength;
ULONG nBytes;
ULONG index;
//
// Create the header for this node
//
nodeHeader.NodeType = Node -> NodeType;
nodeHeader.NumberOfChildren = (UCHAR) Node -> NumberOfChildren;
nodeHeader.NodeInfoExists = (Node -> NodeInformation != NULL);
nodeHeader.ConnectionInfoExists = (Node -> ConnectionInformation != NULL);
nodeHeader.SymbolicLinkLength = (NULL != Node -> SymbolicLink)
? strlen(Node -> SymbolicLink)+1 : 0;
nodeHeader.DescriptionLength = (NULL != Node -> Description)
? strlen(Node -> Description)+1 : 0;
nodeHeader.ClassNameLength = (NULL != Node -> ClassName)
? strlen(Node -> ClassName)+1 : 0;
nodeHeader.DeviceStatus = Node -> DeviceStatus;
nodeHeader.DeviceProblem = Node -> DeviceProblem;
//
// Calculate the size of the variable length section
//
variableLength = CalculateNodeVariableLength(nodeHeader);
bufferLength = sizeof(FILE_NODE_HEADER) + variableLength;
//
// Allocate a data buffer to hold all the information for this node
//
dataBuffer = ALLOC(bufferLength);
if (NULL == dataBuffer)
{
success = FALSE;
goto WriteUSBDeviceNodeToFile_Exit;
}
//
// Copy the necessary data to the buffer
//
bufferWalk = dataBuffer;
CopyMemory(bufferWalk, &nodeHeader, sizeof(FILE_NODE_HEADER));
bufferWalk += sizeof(FILE_NODE_HEADER);
if (nodeHeader.SymbolicLinkLength)
{
CopyMemory(bufferWalk,
Node -> SymbolicLink,
nodeHeader.SymbolicLinkLength);
bufferWalk += nodeHeader.SymbolicLinkLength;
}
if (nodeHeader.DescriptionLength)
{
CopyMemory(bufferWalk,
Node -> Description,
nodeHeader.DescriptionLength);
bufferWalk += nodeHeader.DescriptionLength;
}
if (nodeHeader.ClassNameLength)
{
CopyMemory(bufferWalk,
Node -> ClassName,
nodeHeader.ClassNameLength);
bufferWalk += nodeHeader.ClassNameLength;
}
if (nodeHeader.NodeInfoExists)
{
CopyMemory(bufferWalk,
Node -> NodeInformation,
sizeof(USB_NODE_INFORMATION));
bufferWalk += sizeof(USB_NODE_INFORMATION);
}
if (nodeHeader.ConnectionInfoExists)
{
CopyMemory(bufferWalk,
Node -> ConnectionInformation,
sizeof(USB_NODE_CONNECTION_INFORMATION));
}
success = WriteFile(File,
dataBuffer,
bufferLength,
&nBytes,
NULL);
WriteUSBDeviceNodeToFile_Exit:
if (NULL != dataBuffer)
{
FREE(dataBuffer);
}
return (success);
}
//*****************************************************************************
//
// ReadUSBDeviceNodeFromFile()
//
//*****************************************************************************
PUSB_DEVICE_NODE
ReadUSBDeviceNodeFromFile(
IN HANDLE File
)
{
PUSB_NODE_INFORMATION nodeInfo;
PUSB_NODE_CONNECTION_INFORMATION connectInfo;
PUSB_DEVICE_NODE deviceNode;
PUSB_DEVICE_NODE childNode;
FILE_NODE_HEADER nodeHeader;
BOOL success;
ULONG variableLength;
ULONG nBytes;
ULONG index;
PCHAR dataBuffer;
PCHAR bufferWalk;
PCHAR symbolicLink;
PCHAR description;
PCHAR className;
//
// Initialize the deviceNode to NULL in case of an error
//
deviceNode = NULL;
dataBuffer = NULL;
symbolicLink = NULL;
description = NULL;
className = NULL;
nodeInfo = NULL;
connectInfo = NULL;
//
// Read in the node header
//
success = ReadFile(File,
&nodeHeader,
sizeof(nodeHeader),
&nBytes,
NULL);
if (!success)
{
goto ReadUSBDeviceNodeFromFile_Exit;
}
variableLength = CalculateNodeVariableLength(nodeHeader);
//
// Allocate space for a data buffer
//
dataBuffer = ALLOC(variableLength);
if (NULL == dataBuffer)
{
goto ReadUSBDeviceNodeFromFile_Exit;
}
//
// Read in the variable length portion of the node
//
success = ReadFile(File,
dataBuffer,
variableLength,
&nBytes,
NULL);
if (!success)
{
goto ReadUSBDeviceNodeFromFile_Exit;
}
//
// Allocate space to hold all the variable length information
// for the device node
//
if (nodeHeader.SymbolicLinkLength)
{
symbolicLink = ALLOC(nodeHeader.SymbolicLinkLength);
if (NULL == symbolicLink)
{
goto ReadUSBDeviceNodeFromFile_Exit;
}
}
if (nodeHeader.DescriptionLength)
{
description = ALLOC(nodeHeader.DescriptionLength);
if (NULL == description)
{
goto ReadUSBDeviceNodeFromFile_Exit;
}
}
if (nodeHeader.ClassNameLength)
{
className = ALLOC(nodeHeader.ClassNameLength);
if (NULL == className)
{
goto ReadUSBDeviceNodeFromFile_Exit;
}
}
if (nodeHeader.NodeInfoExists)
{
nodeInfo = ALLOC(sizeof(USB_NODE_INFORMATION));
if (NULL == nodeInfo)
{
goto ReadUSBDeviceNodeFromFile_Exit;
}
}
if (nodeHeader.ConnectionInfoExists)
{
connectInfo = ALLOC(sizeof(USB_NODE_CONNECTION_INFORMATION));
if (NULL == connectInfo)
{
goto ReadUSBDeviceNodeFromFile_Exit;
}
}
//
// Create the device node itself
//
deviceNode = CreateUSBDeviceNode(nodeHeader.NodeType,
nodeHeader.NumberOfChildren);
if (NULL == deviceNode)
{
goto ReadUSBDeviceNodeFromFile_Exit;
}
//
// Setup the deviceNode structure and copy the variable data into the
// buffers that have been allocated for that data
//
bufferWalk = dataBuffer;
if (nodeHeader.SymbolicLinkLength)
{
strcpy(symbolicLink, bufferWalk);
bufferWalk += nodeHeader.SymbolicLinkLength;
}
if (nodeHeader.DescriptionLength)
{
strcpy(description, bufferWalk);
bufferWalk += nodeHeader.DescriptionLength;
}
if (nodeHeader.ClassNameLength)
{
strcpy(className, bufferWalk);
bufferWalk += nodeHeader.ClassNameLength;
}
if (nodeHeader.NodeInfoExists)
{
memcpy(nodeInfo, bufferWalk, sizeof(USB_NODE_INFORMATION));
bufferWalk += sizeof(USB_NODE_INFORMATION);
}
if (nodeHeader.ConnectionInfoExists)
{
memcpy(connectInfo, bufferWalk, sizeof(USB_NODE_CONNECTION_INFORMATION));
}
//
// Setup the fields with the new values
//
deviceNode -> SymbolicLink = symbolicLink;
deviceNode -> Description = description;
deviceNode -> ClassName = className;
deviceNode -> NodeInformation = nodeInfo;
deviceNode -> ConnectionInformation = connectInfo;
deviceNode -> DeviceInstance = 0;
deviceNode -> DeviceStatus = nodeHeader.DeviceStatus;
deviceNode -> DeviceProblem = nodeHeader.DeviceProblem;
//
// NULL the local variables so we don't double free them
//
symbolicLink = NULL;
description = NULL;
className = NULL;
connectInfo = NULL;
nodeInfo = NULL;
//
// Create the child nodes and add to the tree if successful
//
for (index = 0; index < deviceNode -> NumberOfChildren; index++)
{
childNode = ReadUSBDeviceNodeFromFile(File);
//
// If an error occurred, let's bail. But first the deviceNode
// must be destoryed and NULL'd
//
if (NULL == childNode)
{
deviceNode -> NumberOfChildren = index;
DestroyUSBDeviceNode(deviceNode, NULL);
deviceNode = NULL;
break;
}
//
// Add the child node to the deviceNode
//
childNode -> Parent = deviceNode;
deviceNode -> Children[index] = childNode;
}
ReadUSBDeviceNodeFromFile_Exit:
//
// Do any cleanup that might be necessary
//
if (NULL != symbolicLink)
{
FREE(symbolicLink);
}
if (NULL != description)
{
FREE(description);
}
if (NULL != className)
{
FREE(className);
}
if (NULL != nodeInfo)
{
FREE(nodeInfo);
}
if (NULL != connectInfo)
{
FREE(connectInfo);
}
if (NULL != dataBuffer)
{
FREE(dataBuffer);
}
return (deviceNode);
}
//*****************************************************************************
//
// CompareUSBDeviceNodes()
//
//*****************************************************************************
VOID
CompareUSBDeviceNodes(
IN PUSB_DEVICE_NODE Node1,
IN PUSB_DEVICE_NODE Node2,
OUT PULONG NumberOfDifferences,
OUT PNODE_PAIR_LIST NodeDifferenceList
)
{
ULONG index;
//
// Compare the types of the two nodes and the number of children.
// If no match, they can't be the same.
//
if (Node1 -> NodeType != Node2 -> NodeType ||
Node1 -> NumberOfChildren != Node2 -> NumberOfChildren)
{
goto CompareUSBDeviceNodes_NoMatch;
}
//
// The node's DeviceProblem code should also match. The assumption is that
// if the node was once problematic, it should always be problematic.
// Obviously, if it was operating correctly and now isn't that's a definite
// error. Likewise, if there was previously an error, that error should
// continue to persist. If the device starts working, why wasn't it working
// the first time around.
//
// The jury is still out on the status field. Investigation needs to be
// done to insure that all flags don't change across enumerations. It
// appears by the definition of the flags in CFG.H that some may actually
// be different. Until that thought is proven wrong, avoid checking
// the status field as well.
//
if (Node1 -> DeviceProblem != Node2 -> DeviceProblem)
{
goto CompareUSBDeviceNodes_NoMatch;
}
//
// Now do more specific compare based on the type of the nodes
//
switch (Node1 -> NodeType)
{
case Computer:
case HostController:
//
// If the child count matched we are OK, proceed with comparing
// the children
//
break;
case Hub:
//
// For a hub, we want to compare the VID/PID combination, if
// an external hub. If that checks out then check the children
//
if (NULL == Node1 -> ConnectionInformation)
{
//
// Root hub...Node2 also better have this field NULL
//
if (NULL != Node2 -> ConnectionInformation)
{
goto CompareUSBDeviceNodes_NoMatch;
}
}
else
{
//
// External hub on node1...Node2 had better be an
// external hub as well.
//
if (NULL == Node2 -> ConnectionInformation)
{
goto CompareUSBDeviceNodes_NoMatch;
}
//
// OK, now compare the VID/PID of these devices
//
if ( (Node1 -> ConnectionInformation->DeviceDescriptor.idVendor !=
Node2 -> ConnectionInformation->DeviceDescriptor.idVendor) ||
(Node1 -> ConnectionInformation->DeviceDescriptor.idProduct !=
Node2 -> ConnectionInformation->DeviceDescriptor.idProduct))
{
goto CompareUSBDeviceNodes_NoMatch;
}
}
//
// If we got this far, need to compare the children though
//
break;
case MIParent:
//
// For a parent the VID/PID should be the same as well as the
// NumberOfInterfaces for the parent
//
if ( (Node1 -> ConnectionInformation->DeviceDescriptor.idVendor !=
Node2 -> ConnectionInformation->DeviceDescriptor.idVendor) ||
(Node1 -> ConnectionInformation->DeviceDescriptor.idProduct !=
Node2 -> ConnectionInformation->DeviceDescriptor.idProduct))
{
goto CompareUSBDeviceNodes_NoMatch;
}
//
// Compare the number of child interfaces...
//
if (Node1 -> NumberOfChildren != Node2 -> NumberOfChildren)
{
goto CompareUSBDeviceNodes_NoMatch;
}
break;
case Device:
case MIInterface:
//
// For a device the VID/PID should be the same
//
if ( (Node1 -> ConnectionInformation->DeviceDescriptor.idVendor !=
Node2 -> ConnectionInformation->DeviceDescriptor.idVendor) ||
(Node1 -> ConnectionInformation->DeviceDescriptor.idProduct !=
Node2 -> ConnectionInformation->DeviceDescriptor.idProduct))
{
goto CompareUSBDeviceNodes_NoMatch;
}
break;
default:
break;
}
//
// For all cases, we need to check each of the children for the device
// to check the children.
//
for (index = 0; index < Node1 -> NumberOfChildren; index++)
{
CompareUSBDeviceNodes(Node1 -> Children[index],
Node2 -> Children[index],
NumberOfDifferences,
NodeDifferenceList);
}
return;
CompareUSBDeviceNodes_NoMatch:
(*NumberOfDifferences)++;
AddNodePair(Node1, Node2, NodeDifferenceList);
return;
}
//*****************************************************************************
//
// AddNodePair()
//
//*****************************************************************************
VOID
AddNodePair(
IN PUSB_DEVICE_NODE Node1,
IN PUSB_DEVICE_NODE Node2,
IN PNODE_PAIR_LIST NodePairList
)
{
PUSB_DEVICE_NODE *newList;
ULONG newMaxCount;
//
// Check if we need to resize the list
//
if (0 != NodePairList -> MaxNodePairs &&
NodePairList -> MaxNodePairs == NodePairList -> CurrentNodePairs)
{
//
// Need to allocate some more memory, so let's create a new
// list that will hold twice as many pairs
//
newMaxCount = 2 * NodePairList -> MaxNodePairs;
//
// Maximum count is always the count of node pairs so we
// need twice as many node pointers in the list
//
newList = ALLOC(newMaxCount * 2 * sizeof(PUSB_DEVICE_NODE));
//
// If we couldn't resize, oh well, we won't be able to add
// this pair
//
if (NULL == newList)
{
goto AddNodePair_Exit;
}
//
// Copy the pairs over from the previous list
//
CopyMemory(newList,
NodePairList -> NodePairs,
NodePairList -> MaxNodePairs * 2 * sizeof(PUSB_DEVICE_NODE));
//
// Free the previous list
//
FREE(NodePairList -> NodePairs);
//
// Save the new maximum value and the pointer to the new list
//
NodePairList -> MaxNodePairs = newMaxCount;
NodePairList -> NodePairs = newList;
}
//
// Add the new pair and increment the current count
//
NodePairList -> NodePairs[NodePairList -> CurrentNodePairs*2] = Node1;
NodePairList -> NodePairs[NodePairList -> CurrentNodePairs*2+1] = Node2;
NodePairList -> CurrentNodePairs++;
AddNodePair_Exit:
return;
}
//*****************************************************************************
//
// GetRootHubName()
//
//*****************************************************************************
PCHAR GetRootHubName (
HANDLE HostController
)
{
BOOL success;
ULONG nBytes;
USB_ROOT_HUB_NAME rootHubName;
PUSB_ROOT_HUB_NAME rootHubNameW;
PCHAR rootHubNameA;
rootHubNameW = NULL;
rootHubNameA = NULL;
//
// Get the length of the name of the Root Hub attached to the
// Host Controller
//
success = DeviceIoControl(HostController,
IOCTL_USB_GET_ROOT_HUB_NAME,
0,
0,
&rootHubName,
sizeof(rootHubName),
&nBytes,
NULL);
if (!success)
{
goto GetRootHubName_Exit;
}
//
// Allocate space to hold the Root Hub name
//
nBytes = rootHubName.ActualLength;
rootHubNameW = ALLOC(nBytes);
if (rootHubNameW == NULL)
{
goto GetRootHubName_Exit;
}
//
// Get the name of the Root Hub attached to the Host Controller
//
success = DeviceIoControl(HostController,
IOCTL_USB_GET_ROOT_HUB_NAME,
NULL,
0,
rootHubNameW,
nBytes,
&nBytes,
NULL);
if (!success)
{
goto GetRootHubName_Exit;
}
//
// Convert the Root Hub name
//
rootHubNameA = WideStrToMultiStr(rootHubNameW->RootHubName);
GetRootHubName_Exit:
//
// Free the rootHubNameW structure if it has been allocated
//
if (rootHubNameW != NULL)
{
FREE(rootHubNameW);
rootHubNameW = NULL;
}
return rootHubNameA;
}
//*****************************************************************************
//
// GetExternalHubName()
//
//*****************************************************************************
PCHAR
GetExternalHubName (
HANDLE HubHandle,
ULONG ConnectionIndex
)
{
BOOL success;
ULONG nBytes;
USB_NODE_CONNECTION_NAME externalHubName;
PUSB_NODE_CONNECTION_NAME externalHubNameW;
PCHAR externalHubNameA;
externalHubNameW = NULL;
externalHubNameA = NULL;
///
// Get the length of the name of the device hub attached to the
// specified port.
//
externalHubName.ConnectionIndex = ConnectionIndex;
success = DeviceIoControl(HubHandle,
IOCTL_USB_GET_NODE_CONNECTION_NAME,
&externalHubName,
sizeof(externalHubName),
&externalHubName,
sizeof(externalHubName),
&nBytes,
NULL);
if (!success)
{
goto GetExternalHubName_Exit;
}
//
// Allocate space to hold the externalHubName
//
nBytes = externalHubName.ActualLength;
if (nBytes <= sizeof(externalHubName))
{
goto GetExternalHubName_Exit;
}
externalHubNameW = ALLOC(nBytes);
if (externalHubNameW == NULL)
{
goto GetExternalHubName_Exit;
}
//
// Get the name of the device attached to the specified port
//
externalHubNameW -> ConnectionIndex = ConnectionIndex;
success = DeviceIoControl(HubHandle,
IOCTL_USB_GET_NODE_CONNECTION_NAME,
externalHubNameW,
nBytes,
externalHubNameW,
nBytes,
&nBytes,
NULL);
if (!success)
{
goto GetExternalHubName_Exit;
}
//
// Convert the device name
//
externalHubNameA = WideStrToMultiStr(externalHubNameW->NodeName);
GetExternalHubName_Exit:
//
// All done, free the uncoverted device hub name and return the
// converted device name
//
if (externalHubNameW != NULL)
{
FREE(externalHubNameW);
}
return (externalHubNameA);
}
//*****************************************************************************
//
// GetDriverKeyName()
//
//*****************************************************************************
PCHAR
GetDriverKeyName (
HANDLE HubHandle,
ULONG ConnectionIndex
)
{
BOOL success;
ULONG nBytes;
USB_NODE_CONNECTION_DRIVERKEY_NAME driverKeyName;
PUSB_NODE_CONNECTION_DRIVERKEY_NAME driverKeyNameW;
PCHAR driverKeyNameA;
driverKeyNameW = NULL;
driverKeyNameA = NULL;
///
// Get the length of the name of the device hub attached to the
// specified port.
//
driverKeyName.ConnectionIndex = ConnectionIndex;
success = DeviceIoControl(HubHandle,
IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME,
&driverKeyName,
sizeof(driverKeyName),
&driverKeyName,
sizeof(driverKeyName),
&nBytes,
NULL);
if (!success)
{
goto GetDriverKeyName_Exit;
}
//
// Allocate space to hold the driverKeyName
//
nBytes = driverKeyName.ActualLength;
if (nBytes < sizeof(driverKeyName))
{
goto GetDriverKeyName_Exit;
}
driverKeyNameW = ALLOC(nBytes);
if (driverKeyNameW == NULL)
{
goto GetDriverKeyName_Exit;
}
//
// Get the name of the device attached to the specified port
//
driverKeyNameW -> ConnectionIndex = ConnectionIndex;
success = DeviceIoControl(HubHandle,
IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME,
driverKeyNameW,
nBytes,
driverKeyNameW,
nBytes,
&nBytes,
NULL);
if (!success)
{
goto GetDriverKeyName_Exit;
}
//
// Convert the device name
//
driverKeyNameA = WideStrToMultiStr(driverKeyNameW->DriverKeyName);
GetDriverKeyName_Exit:
//
// All done, free the uncoverted device hub name and return the
// converted device name
//
if (driverKeyNameW != NULL)
{
FREE(driverKeyNameW);
}
return (driverKeyNameA);
}