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

574 lines
13 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 SLEEP_TIME 10000
#define VER_PRODUCTVERSION_STR "0.90"
#define SURPRISE_REMOVE_WINNAME "Unsafe Removal of Device"
typedef struct _CYCLE_TEST_CONTEXT
{
PUSB_DEVICE_TREE LastKnownTree;
} CYCLE_TEST_CONTEXT, *PCYCLE_TEST_CONTEXT;
//
// Global variables
//
ULONG TestIterations = 1;
BOOL CycleHubs = FALSE;
BOOL Verbose = FALSE;
BOOL Abort;
//
// Local Function Declarations
//
BOOL
ParseArgs (
int argc,
char *argv[]
);
VOID
Usage(
VOID
);
VOID
Version(
VOID
);
//
// Some Local function definitions
//
VOID
DisplayDifferentNode(
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:
printf("Computer: NumberOfHCs = %d",
Node -> NumberOfChildren);
break;
case HostController:
printf("Host Controller: %s",
Node -> Description);
case Hub:
if (NULL == Node -> NodeInformation)
{
printf("Host Controller %s --> RootHub: %d ports",
Parent -> Description,
Node -> NumberOfChildren);
}
else
{
printf("Hub: %s --> %d ports",
Node -> Description,
Node -> NumberOfChildren);
}
break;
case MIParent:
printf("%s port %d --> %s: %d interfaces",
Parent -> Description,
ParentIndex+1,
Node -> Description,
Node -> NumberOfChildren);
break;
case MIInterface:
printf("%s interface --> %s",
Parent -> Description,
Node -> Description);
break;
case Device:
printf("%s port %d --> %s",
Parent -> Description,
ParentIndex+1,
Node -> Description);
break;
case EmptyPort:
printf("%s port %d --> EmptyPort",
Parent -> Description,
ParentIndex+1);
break;
}
if (0 != Node -> DeviceProblem)
{
printf(" (!:%d)", Node -> DeviceProblem);
}
printf("\n");
return;
}
VOID
DisplayDifferenceList(
ULONG NumDifferences,
PNODE_PAIR_LIST DifferenceList
)
{
ULONG index;
if (0 != NumDifferences)
{
printf("Node Differences (%d)\n"
"--------------------\n",
NumDifferences);
for (index = 0; index < DifferenceList -> CurrentNodePairs; index++)
{
printf("\n"
"Node Pair %d\n"
"-----------\n",
index+1);
DisplayDifferentNode(DifferenceList -> NodePairs[index*2]);
DisplayDifferentNode(DifferenceList -> NodePairs[index*2+1]);
}
}
return;
}
VOID
DismissSurpriseRemovals(
VOID
)
{
TCHAR surpriseRemoveWinName[] = _T(SURPRISE_REMOVE_WINNAME);
HWND surpriseRemoveWindow;
HWND prevWindow;
ULONG retryCount;
BOOL success;
//
// To try to dismiss this window, we search first try to find an instance
// of this window and then post a message to the window to shut itself
// down.
//
prevWindow = INVALID_HANDLE_VALUE;
while (1)
{
retryCount = 0;
do
{
surpriseRemoveWindow = FindWindow(WC_DIALOG, surpriseRemoveWinName);
}
while (surpriseRemoveWindow == prevWindow && retryCount++ < 3);
if (NULL == surpriseRemoveWindow || surpriseRemoveWindow == prevWindow)
{
//
// There are either no more windows or we keep getting the
// same window returned to us. We'll break out of the loop now
// just to make sure we don't hang this test trying to close
// the same non-responding window over and over.
//
if (surpriseRemoveWindow == prevWindow)
{
OutputDebugString("CYCLER: Keep getting the same removal window\n");
OutputDebugString(" Either window isn't responding or\n");
OutputDebugString(" sleep time needs to be increased\n");
}
break;
}
//
// Found a new window let's post a message to close it
//
// NOTE: PostMessage() doesn't always post the message if that
// window's messageQueue happens to be full. We will only try
// this 3 times though and then give up...
//
retryCount = 0;
do
{
success = PostMessage(surpriseRemoveWindow,
WM_COMMAND,
IDOK,
0);
} while (!success && retryCount++ < 3);
//
// If successful, wait for the window to shutdown
//
if (success)
{
Sleep(500);
}
prevWindow = surpriseRemoveWindow;
}
return;
}
BOOLEAN
CycleTestCallbackPre(
IN PUSB_DEVICE_NODE Node,
IN PCYCLE_TEST_CONTEXT Context
)
{
return (TRUE);
}
BOOLEAN
CycleTestCallbackPost(
IN PUSB_DEVICE_NODE Node,
IN PCYCLE_TEST_CONTEXT Context
)
{
BOOLEAN success;
ULONG nBytes;
PUSB_DEVICE_TREE currentTree;
ULONG numDifferences;
NODE_PAIR_LIST differenceList;
success = TRUE;
//
// Determine if this port should be cycled. If cycleHubs was specified,
// then we cycle any USBDevice (NonHub and ExternalHub). Otherwise,
// just cycle the port if a NonHubDevice is attached (composite/regular)
//
if ( (IsNonHubDevice(Node)) || (CycleHubs && IsExternalHub(Node)))
{
printf("Attempting to cycle %s\n", Node -> Description);
success = CycleUSBPort(Node);
if (!success)
{
printf("Error %d occurred trying to cycle the port!\n",
GetLastError());
}
else
{
printf("Cycle completed with success\n");
printf("Sleeping for %d ms\n", SLEEP_TIME);
Sleep(SLEEP_TIME);
//
// Dismiss the surprise removal dialog box that might show up
//
DismissSurpriseRemovals();
currentTree = BuildUSBDeviceTree();
if (NULL == currentTree)
{
printf("Error %d occurred trying to build current tree\n"
"Comparison after cycle port not done\n",
GetLastError());
success = FALSE;
}
else
{
printf("Comparing the current tree with the last tree\n");
InitializeNodePairList(&differenceList);
success = CompareUSBDeviceTrees(Context -> LastKnownTree,
currentTree,
&numDifferences,
&differenceList);
if (0 != numDifferences)
{
printf("There are %d difference between device trees"
"after port cycle\n",
numDifferences);
DisplayDifferenceList(numDifferences, &differenceList);
Context -> LastKnownTree = currentTree;
}
else
{
printf("Device trees are the same after port cycle\n");
}
DestroyUSBDeviceTree(currentTree);
DestroyNodePairList(&differenceList);
}
}
}
return (success);
}
BOOL
DoTreeCycle(
ULONG IterationNumber
)
{
CYCLE_TEST_CONTEXT context;
BOOL success;
printf("Starting cycle iteration %d\n", IterationNumber);
success = TRUE;
context.LastKnownTree = BuildUSBDeviceTree();
if (NULL == context.LastKnownTree)
{
printf("Error %d occurred building the current device tree\n",
GetLastError());
success = FALSE;
goto DoTreeCycle_Exit;
}
success = WalkUSBDeviceTree(context.LastKnownTree,
CycleTestCallbackPre,
CycleTestCallbackPost,
&context);
DestroyUSBDeviceTree(context.LastKnownTree);
DoTreeCycle_Exit:
printf("Ending cycle iteration %d\n", IterationNumber);
return (success);
}
//*****************************************************************************
//
// CtrlHandlerRoutine()
//
//*****************************************************************************
BOOL WINAPI
CtrlHandlerRoutine (
DWORD dwCtrlType
)
{
BOOL handled;
switch (dwCtrlType)
{
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
Abort = TRUE;
handled = TRUE;
break;
default:
handled = FALSE;
break;
}
return handled;
}
int __cdecl
main(
int argc,
char *argv[]
)
{
ULONG index;
BOOL success;
if (!ParseArgs(argc, argv))
{
return (1);
}
//
// Set a CTRL-C / CTRL-BREAK handler
//
Abort = FALSE;
SetConsoleCtrlHandler(CtrlHandlerRoutine, TRUE);
for (index = 1; index <= TestIterations && !Abort; index++)
{
success = DoTreeCycle(index);
if (!success)
{
printf("Cycle test failed on iteration %d\n", index);
break;
}
}
if (Abort)
{
printf("Cycle test aborted!\n");
}
//
// 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, j;
if (argc < 2)
{
Usage();
return FALSE;
}
for (i=1; i<argc; i++)
{
if (argv[i][0] == '-' || argv[i][0] == '/')
{
switch(argv[i][1])
{
case 'H':
case 'h':
CycleHubs = TRUE;
break;
case 'I':
case 'i':
if ('\0' != argv[i][2])
{
TestIterations = atoi(&argv[i][2]);
}
else
{
i++;
if (i == argc)
{
Usage();
return FALSE;
}
else
{
TestIterations = atoi(argv[i]);
}
}
break;
case '?':
Usage();
return (FALSE);
case 'V':
case 'v':
Verbose = TRUE;
break;
default:
Usage();
return FALSE;
}
}
}
//
// Dump parsed args if desired
//
if (Verbose)
{
printf( "Iterations: %d\n", TestIterations);
printf( "Verbose: %d\n", Verbose);
}
return TRUE;
}
/*****************************************************************************
//
// Usage()
//
//****************************************************************************/
VOID
Usage(
VOID
)
{
Version();
printf( "usage:\n");
printf( "-h cycle ports with attached hubs\n");
printf( "-i [n] where n is the number of iterations to perform\n");
printf( "-V verbose output\n");
}
/*****************************************************************************
//
// Version()
//
//****************************************************************************/
void
Version ()
{
printf( "CYCLER.EXE %s\n", VER_PRODUCTVERSION_STR);
return;
}