574 lines
13 KiB
C
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;
|
|
}
|