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

632 lines
15 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <wtypes.h>
#include <tchar.h>
#include <winioctl.h>
#include <usbioctl.h>
#include <cfgmgr32.h>
#include "usbtest.h"
#define VER_PRODUCTVERSION_STR "0.90"
#define MIN_ARGS 2
#define DEFAULT_TREEFILE_NAME ".\\usbtree.bin"
#define NEWLINE "\r\n"
#define TREELOG_EXIT(s) { success = (s); goto Treelog_Exit; }
//
// Global variables
//
BOOL BreakOnError = FALSE;
BOOL Verbose = FALSE;
BOOL LogTree = FALSE;
BOOL NewTree = FALSE;
BOOL DumpDescriptions = FALSE;
BOOL FileNameSet = FALSE;
CHAR LogFileName[MAX_PATH+1] = "";
//
// Local Function Declarations
//
BOOL
ParseArgs (
int argc,
char *argv[]
);
VOID
Usage(
VOID
);
VOID
Version(
VOID
);
//
// Some Local function definitions
//
BOOL
WriteLog(
IN HANDLE logFile,
IN PSTR formatString,
...
)
{
CHAR logMsg[256];
DWORD bytesWritten;
BOOL success;
va_list args;
va_start(args, formatString);
vsprintf(logMsg, formatString, args);
success = WriteFile(logFile,
logMsg,
strlen(logMsg),
&bytesWritten,
NULL);
return (success);
}
VOID
DisplayDifferentNode(
HANDLE LogFile,
PUSB_DEVICE_NODE Node
)
{
PUSB_DEVICE_NODE Parent;
ULONG ParentIndex;
Parent = Node -> Parent;
if (NULL != Parent)
{
ParentIndex = 0;
while (Parent -> Children[ParentIndex] != Node)
{
ParentIndex++;
}
}
switch (Node -> NodeType)
{
case Computer:
WriteLog(LogFile,
"Computer: NumberOfHCs = %d" NEWLINE,
Node -> NumberOfChildren);
break;
case HostController:
WriteLog(LogFile,
"Host Controller: %s" NEWLINE,
Node -> Description);
case Hub:
if (NULL == Node -> NodeInformation)
{
WriteLog(LogFile,
"Host Controller %s --> RootHub: %d ports" NEWLINE,
Parent -> Description,
Node -> NumberOfChildren);
}
else
{
WriteLog(LogFile,
"Hub: %s --> %d ports" NEWLINE,
Node -> Description,
Node -> NumberOfChildren);
}
break;
case MIParent:
WriteLog(LogFile,
"%s port %d --> %s: %d interfaces" NEWLINE,
Parent -> Description,
ParentIndex+1,
Node -> Description,
Node -> NumberOfChildren);
break;
case MIInterface:
printf("%s interface --> %s\n",
Parent -> Description,
Node -> Description);
break;
case Device:
WriteLog(LogFile,
"%s port %d --> %s" NEWLINE,
Parent -> Description,
ParentIndex+1,
Node -> Description);
break;
case EmptyPort:
WriteLog(LogFile,
"%s port %d --> EmptyPort" NEWLINE,
Parent -> Description,
ParentIndex+1);
break;
}
return;
}
VOID
DisplayDifferenceList(
HANDLE LogFile,
ULONG NumDifferences,
PNODE_PAIR_LIST DifferenceList
)
{
ULONG index;
if (0 != NumDifferences)
{
printf("Node Differences (%d)" NEWLINE
"--------------------" NEWLINE,
NumDifferences);
for (index = 0; index < DifferenceList -> CurrentNodePairs; index++)
{
WriteLog(LogFile, NEWLINE
"Node Pair %d" NEWLINE
"-----------" NEWLINE,
index+1);
DisplayDifferentNode(LogFile, DifferenceList -> NodePairs[index*2]);
DisplayDifferentNode(LogFile, DifferenceList -> NodePairs[index*2+1]);
}
}
return;
}
BOOLEAN
WriteDescriptionsCallbackPre(
IN PUSB_DEVICE_NODE Node,
IN HANDLE * Context
)
{
HANDLE logFile;
logFile = *Context;
switch (Node -> NodeType)
{
case HostController:
case Hub:
case MIParent:
case MIInterface:
case Device:
WriteLog(logFile,
"%s",
Node -> Description);
if (NULL != Node -> ConnectionInformation)
{
WriteLog(logFile,
" %04X %04X",
Node -> ConnectionInformation -> DeviceDescriptor.idVendor,
Node -> ConnectionInformation -> DeviceDescriptor.idProduct);
}
WriteLog(logFile, NEWLINE);
break;
default:
break;
}
return (TRUE);
}
BOOL
WriteDescriptionsToLogFile(
IN HANDLE LogFile,
IN PUSB_DEVICE_TREE Tree
)
{
BOOL success;
success = WalkUSBDeviceTree(Tree,
WriteDescriptionsCallbackPre,
NULL,
&LogFile);
return (success);
}
int __cdecl
main(
int argc,
char *argv[]
)
{
ULONG index;
BOOL success;
HANDLE logFile;
HANDLE treeFile;
DWORD treeFileSize;
PUSB_DEVICE_TREE currentUSBTree;
PUSB_DEVICE_TREE savedUSBTree;
SYSTEMTIME localTime;
NODE_PAIR_LIST nodeList;
ULONG numNodes;
//
// Initialize, so that cleanup occurs appropriately on exit.
//
logFile = INVALID_HANDLE_VALUE;
treeFile = INVALID_HANDLE_VALUE;
currentUSBTree = NULL;
savedUSBTree = NULL;
//
// Try to parse the args
//
if (!ParseArgs(argc, argv))
{
TREELOG_EXIT(FALSE);
}
//
// Try to open the log file...Specify sharing flags of 0 to get exclusive
// access to this file.
//
#if 1
logFile = CreateFile(LogFileName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_ALWAYS,
FILE_FLAG_WRITE_THROUGH,
NULL);
#else
logFile = GetStdHandle(STD_OUTPUT_HANDLE);
#endif
if (INVALID_HANDLE_VALUE == logFile)
{
TREELOG_EXIT(FALSE);
}
SetFilePointer(logFile, 0, NULL, FILE_END);
//
// Get and log the time
//
GetLocalTime(&localTime);
success = WriteLog(logFile,
"%02d/%02d/%2d: %02d:%02d:%02d" NEWLINE,
localTime.wMonth,
localTime.wDay,
localTime.wYear,
localTime.wHour,
localTime.wMinute,
localTime.wSecond);
currentUSBTree = BuildUSBDeviceTree();
if (NULL == currentUSBTree)
{
WriteLog(logFile,
"Error %d occurred building current USB device tree" NEWLINE,
GetLastError());
TREELOG_EXIT(FALSE);
}
if (LogTree)
{
WriteUSBDeviceTreeToTextFile(logFile, currentUSBTree);
}
if (DumpDescriptions)
{
WriteDescriptionsToLogFile(logFile, currentUSBTree);
TREELOG_EXIT(TRUE);
}
treeFile = CreateFile(DEFAULT_TREEFILE_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_ALWAYS,
FILE_FLAG_WRITE_THROUGH,
NULL);
if (INVALID_HANDLE_VALUE == treeFile)
{
WriteLog(logFile,
"Error %d occurred trying to open tree file" NEWLINE,
GetLastError());
TREELOG_EXIT(FALSE);
}
//
// Get the size of the tree file
//
treeFileSize = GetFileSize(treeFile, NULL);
//
// Write a new tree structure to the file if the file size
//
if (NewTree || 0 == treeFileSize)
{
success = WriteUSBDeviceTreeToFile(treeFile, currentUSBTree);
if (!success)
{
WriteLog(logFile,
"Error %d occurred writing tree to tree file" NEWLINE,
GetLastError());
TREELOG_EXIT(FALSE);
}
//
// Reset the file pointer to the beginning of the file
//
SetFilePointer(treeFile, 0, NULL, FILE_BEGIN);
}
//
// Retrieve the USB Tree that is stored in the file
//
success = ReadUSBDeviceTreeFromFile(treeFile, &savedUSBTree);
if (!success)
{
WriteLog(logFile,
"Error %d occurred reading saved device tree from tree file" NEWLINE,
GetLastError());
TREELOG_EXIT(FALSE);
}
//
// Add comparison of savedUSBTree to currentUSBTree and
// if different, log the node pairs....
//
InitializeNodePairList(&nodeList);
success = CompareUSBDeviceTrees(currentUSBTree,
savedUSBTree,
&numNodes,
&nodeList);
//
// If success is FALSE, then there are some differences in the tree
// write those differences to the log file.
//
if (!success)
{
WriteLog(logFile, "Failed device tree comparison" NEWLINE);
DisplayDifferenceList(logFile, numNodes, &nodeList);
//
// If the caller set break on error, then we need to break into the
// debugger...
//
if (BreakOnError)
{
OutputDebugString("USB Device tree comparison failed" NEWLINE);
DebugBreak();
}
}
else
{
WriteLog(logFile, "Pass: Trees are identical" NEWLINE);
}
DestroyNodePairList(&nodeList);
Treelog_Exit:
if (INVALID_HANDLE_VALUE != treeFile)
{
CloseHandle(treeFile);
}
if (INVALID_HANDLE_VALUE != logFile)
{
CloseHandle(logFile);
}
if (NULL != currentUSBTree)
{
DestroyUSBDeviceTree(currentUSBTree);
}
if (NULL != savedUSBTree)
{
DestroyUSBDeviceTree(savedUSBTree);
}
//
// Return an errorlevel code of 1 if the test failed and some point
//
return (success ? 0 : 1);
}
//*****************************************************************************
//
// ParseArgs()
//
//*****************************************************************************
BOOL
ParseArgs (
int argc,
char *argv[]
)
{
int i;
char *filename;
if (argc < MIN_ARGS)
{
Usage();
return FALSE;
}
for (i=1; i<argc; i++)
{
if (argv[i][0] == '-' || argv[i][0] == '/')
{
switch(argv[i][1])
{
case 'B':
case 'b':
BreakOnError = TRUE;
break;
case 'D':
case 'd':
DumpDescriptions = TRUE;
break;
case 'F':
case 'f':
if ('\0' != argv[i][2])
{
filename = argv[i]+2;
}
else
{
i++;
if (i == argc)
{
Usage();
return FALSE;
}
filename = argv[i];
}
if (strlen(filename) > MAX_PATH)
{
Usage();
return FALSE;
}
strcpy(LogFileName, filename);
FileNameSet = TRUE;
break;
case 'L':
case 'l':
LogTree = TRUE;
break;
case 'N':
case 'n':
NewTree = TRUE;
break;
case '?':
case 'H':
case 'h':
Usage();
return (FALSE);
case 'V':
case 'v':
Verbose = TRUE;
break;
default:
Usage();
return FALSE;
}
}
}
//
// Dump parsed args if desired
//
if (Verbose)
{
printf( "BreakOnError: %d" NEWLINE, BreakOnError);
printf( "LogTree: %d" NEWLINE, LogTree);
printf( "NewTree: %d" NEWLINE, NewTree);
printf( "LogFileName: %s" NEWLINE, LogFileName);
printf( "Verbose: %d" NEWLINE, Verbose);
}
return TRUE;
}
/*****************************************************************************
//
// Usage()
//
//****************************************************************************/
VOID
Usage(
VOID
)
{
Version();
printf( "usage:" NEWLINE);
printf( "-b break into debugger on error" NEWLINE);
printf( "-d dump a list of device descriptions" NEWLINE);
printf( "-f filename log file name and path" NEWLINE
" filename must be less than %d chars" NEWLINE,
MAX_PATH);
printf( "-l log current tree to log file" NEWLINE);
printf( "-n create a new tree for comparison" NEWLINE);
printf( "-V verbose output" NEWLINE);
}
/*****************************************************************************
//
// Version()
//
//****************************************************************************/
void
Version ()
{
printf( "TREELOG.EXE %s" NEWLINE, VER_PRODUCTVERSION_STR);
return;
}