kerneltest/e32test/usb/t_usb_win/src/t_usb_wintests.cpp
changeset 0 a41df078684a
child 271 dc268b18d709
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/usb/t_usb_win/src/t_usb_wintests.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,2544 @@
+// Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// e32test\usb\t_usb_win\src\t_usb_wintests.cpp
+// Win32 USB test program performs I/O with a device running the
+// Symbian OS test program T_USB_DEVICE, using the generic USB device driver USBIO.
+// === Includes ===
+// 
+//
+
+#include "stdafx.h"
+#include <process.h>    /* _beginthread, _endthread */
+
+#include <sys/timeb.h>
+
+#include "usbio.h"											// USBIO Dev Kit
+#include "usbiopipe.h"										// ditto
+
+#include "t_usb_win.h"
+#include "t_usb_winDlg.h"
+
+#include "global.h"
+
+#include "UsbcvControl.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+extern BOOL EjectVolume(char driveLetter);
+extern BOOL WaitDriveReady (char driveLetter);
+extern DWORD PerlScript(char * scriptName);
+
+// === Global Vars ===
+
+extern char gScriptFileName[];
+extern char gManufacturerName[];
+extern char gProductName[];
+extern char gSerialNumber[];
+extern char gLogfileName[];
+extern BOOL gVerboseMode;
+extern BOOL gKeepOpen;
+extern BOOL gAbort;
+extern int gTransferMode;
+extern CT_usb_winDlg* gMainWindow;
+extern FILE *logStream;
+extern int gReadWriteTimeOut;
+
+// Our own private GUID (also used in the .inf file as 'DriverUserInterfaceGuid')
+// {55606403-E62D-4707-9F56-40D48C6736D0}
+GUID gUsbioID =
+	{0x55606403, 0xe62d, 0x4707, {0x9f, 0x56, 0x40, 0xd4, 0x8c, 0x67, 0x36, 0xd0}};
+
+// Another provate GUID used in the AtenSwitch.inf file  
+// {A6942415-49EE-4ef2-9CE8-D33E888EF133}
+const GUID gSwitchID =
+	{0xA6942415, 0x49EE, 0x4ef2, {0x9C, 0xE8, 0xD3, 0x3E, 0x88, 0x8E, 0xF1, 0x33}};
+
+// Handle for USB device list
+static HDEVINFO gDevList = NULL;
+
+// USBIO supported device
+CUsbIo gUsbDev;
+
+static int gDevNumber = -1;
+static UCHAR gEndpointMap [MAX_INTERFACES] [MAX_SETTINGS] [MAX_ENDPOINTS];
+static WORD gPacketSizeMap [MAX_INTERFACES] [MAX_SETTINGS] [MAX_ENDPOINTS];
+static UCHAR gAlternateSettings [MAX_INTERFACES];
+static bool gIsoEndpoint [MAX_INTERFACES] [MAX_SETTINGS] [MAX_ENDPOINTS];
+
+static USHORT vendorId = 0;
+static USHORT productId = 0;
+static USHORT productIdMS = 0;
+
+// endpoints
+
+// Read/write buffer
+static const DWORD KMaxTransferSize = 1024*1024;
+static const DWORD KPreambleLength = 8;
+
+static bool gFinishAllTests = false;
+
+static HANDLE gThreadStartedEvent; 
+static HANDLE gThreadEndedEvents[MAX_THREADS]; 
+static HANDLE gThreadWaitEvents[MAX_THREADS]; 
+
+HANDLE gDeviceConnectEvent; 
+HANDLE gDeviceDisconnectEvent;
+
+static __int64 gDeviceIdleCount = -1;
+static struct _timeb gHostTime;
+
+enum
+	{
+	EXml,
+	ELoop,
+	ECompare,
+	EReceiveOnly,
+	ETransmitOnly,
+	EFileReceive,
+	EFileTransmit,
+	EComment,
+	EConnect,
+	EDisconnect,
+	EWait,
+	EFail,
+	ESwitch,
+	EWaitConnect,
+	EWaitDisconnect,
+	EDo,
+	ERepeat,
+	EUsbcv,
+	EMassStorage,
+	EEject,
+	EPerl,
+	EWaitDrive,
+	};
+
+enum Ep0Requests
+	{
+	EStop = 0x1,
+	EVersion = 0x10,
+	ETestParam = 0x20,
+	ETestResult = 0x30,
+	ETestFail = 0x40,
+	ETestConnect = 0x50,
+	ETestDisconnect = 0x60,
+	ETestMassStorage = 0x70,
+	ETestIdleCounter = 0x80,
+	};
+
+struct TestParamHost
+	{
+	WORD displayRate;
+	DWORD outRate;
+	DWORD inRate;
+	BYTE index;
+	bool signalNextSettingThread;
+	};
+typedef struct TestParamHost TestParamHostType;
+
+struct TestParam
+	{
+	DWORD minSize;
+	DWORD maxSize;
+	DWORD packetNumber;
+	BYTE interfaceNumber;
+	BYTE alternateSetting;
+	BYTE outPipe;
+	BYTE inPipe;
+	WORD repeat;
+	WORD settingRepeat;
+	BYTE beforeIndex;
+	BYTE afterIndex;
+	TestParamHostType host;
+	TestParam ();
+	};
+typedef struct TestParam TestParamType;
+typedef TestParamType* TestParamPtr;
+
+// The version of T_USB we require (at least)
+static const DWORD KTusbVersionMajor = 1;
+static const DWORD KTusbVersionMinor = 0;
+static const DWORD KTusbVersionMicro = 1;
+
+static const DWORD KHostLogFileSize = 64*1024;
+
+#define ERR_SCRIPT_NAME		0xF0000001L
+
+// use a timeout for a WaitConnect to activate a scan for hardware changes
+#define CONNECT_RESCAN_TIMEOUT	300000
+#define RESCAN_APPNAME "C:\\Program Files\\devcon\\i386\\devcon.exe"
+#define RESCAN_CMDLINE " rescan"
+
+/////////////////////////////////////////////////////////////////////////////
+// global functions
+
+//
+// print to output window
+//
+// Note:
+// This is a global function that is called from any thread context.
+// It is not possible to call MFC objects from a thread context that is
+// created by using non-MFC means.
+// So we use SendMessage to access the OutputWindow.
+//
+void PrintOut(BOOL screenFlag, BOOL logFlag, BOOL timeFlag, const char *format, ...)
+{
+	if (screenFlag)
+	{
+		const int buffersize = 512;
+		va_list argptr;
+  
+		char buffer[buffersize];
+
+		// print to buffer
+		va_start(argptr,format);
+		_vsnprintf(buffer,buffersize-1,format,argptr);
+		buffer[buffersize-1] = 0;
+
+		// with a time stamp and carriage return linefeed 
+		if (timeFlag)
+		{
+			SYSTEMTIME localTime;
+
+			GetLocalTime(&localTime);
+
+			unsigned int len = strlen(buffer);
+
+			GetDateFormat(LOCALE_USER_DEFAULT,0,&localTime,"dd MMM yyyy",&buffer[len],buffersize-len);
+
+			len = strlen(buffer);
+
+			GetTimeFormat(LOCALE_USER_DEFAULT,0,&localTime," H:mm:ss"NL,&buffer[len],buffersize-len);
+		}
+
+		// send buffer to output window (synchronous behavior for buffer protection)
+		gMainWindow->SendMessage(WM_USER_PRINTOUT,0,(LPARAM)buffer); \
+
+		if (logStream && logFlag)
+			fputs(buffer,logStream);
+
+		va_end(argptr);
+	}
+}
+
+static void Delay(int milliseconds)
+	{
+	PRINT_IF_VERBOSE "* Short wait... ");
+	Sleep(milliseconds);
+	PRINT_IF_VERBOSE "done"NL);
+	}
+
+TestParam::TestParam()
+	{
+	minSize = 10;
+	maxSize = 100;
+	packetNumber = 0;
+	interfaceNumber = 0;
+	alternateSetting = 0;
+	outPipe = 2;
+	inPipe = 1;
+	repeat = 0;
+	settingRepeat = 0;
+	beforeIndex = 0;
+	host.displayRate = 100;
+	host.outRate = 0;
+	host.inRate = 0;
+	host.index = 0;
+	host.signalNextSettingThread = FALSE;
+	}
+//
+// Process a line from the test script file
+// returns the test type and sets up the test parameter structure
+//
+static int ParseTestLine(char* aLine,TestParamPtr aParam, USBIO_CLASS_OR_VENDOR_REQUEST* aRequest)
+	{
+	int testType = EComment;
+
+	struct testKey
+		{
+		int testId;
+		char testString[20];
+		};
+	typedef struct testKey testKeyType;
+	size_t tokenLength = 0;
+
+	testKeyType testKeyTable[] =
+		{
+		EXml,"Xml",
+		ELoop,"Loop",
+		ECompare,"Compare",
+		EReceiveOnly,"Stream",
+		EFileReceive,"File",
+		EConnect,"Connect",
+		EDisconnect,"Disconnect",
+		EWait,"Wait",
+		EFail,"Fail",
+		ESwitch,"Switch",
+		EWaitConnect,"WaitConnect",
+		EWaitDisconnect, "WaitDisconnect",
+		ERepeat, "Repeat",
+		EDo, "Do",
+		EUsbcv, "Usbcv",
+		EMassStorage, "MStore",
+		EEject, "Eject",
+		EPerl, "Perl",
+		EWaitDrive, "WaitDrive",
+		};
+
+	size_t  numKeys = sizeof( testKeyTable ) / sizeof( testKeyType );
+
+	char delim[]   = " ,\t\n";
+	char stringDelim[]   = "\"";
+	char * token = strtok(aLine,delim);
+	if (token == NULL)
+		{
+		return testType;
+		}
+
+	tokenLength = strlen(token);
+
+	memset (aParam,0,sizeof(TestParamType));
+
+	// default display rate 
+	aParam->host.displayRate = 100;
+
+	for (int i = 0; i < (int)numKeys; i++)
+		{
+		if (strnicmp (token,testKeyTable[i].testString,tokenLength) == 0)
+			{
+			testType = testKeyTable[i].testId;
+			break;
+			}
+		}
+
+
+	int paramCount = 0;
+	// now set up test type in request setup packet
+	switch (testType)
+		{
+	case EXml:
+		aRequest->Value = 'X';
+		paramCount = 2;
+		break;
+
+	case ELoop:
+		aRequest->Value = 'L';
+		break;
+
+	case ECompare:
+		aRequest->Value = 'C';
+		break;
+
+	case EReceiveOnly:
+		aRequest->Value = 'S';
+		paramCount = 1;
+		break;
+
+	case EFileReceive:
+		aRequest->Value = 'F';
+		paramCount = 1;
+		break;
+
+	case EComment:
+		return testType;
+		break;
+
+	case EConnect:
+		aRequest->Value = 'R';
+		break;
+
+	case EDisconnect:
+		aRequest->Value = 'D';
+		break;
+
+	case EWait:
+		aParam->minSize = INFINITE;
+		break;
+
+	case EWaitConnect:
+		aParam->minSize = INFINITE;
+		break;
+
+	case EWaitDisconnect:
+		aParam->minSize = INFINITE;
+		break;
+
+	case EFail:
+	case ESwitch:
+	case EDo:
+	case ERepeat:
+	case EUsbcv:
+	case EMassStorage:
+	case EEject:
+	case EPerl:
+	case EWaitDrive:
+		break;
+
+		}
+
+	if (testType == EPerl)
+		{
+		// a space at the beginning of the line
+		aLine[0] = ' ';
+		strcpy(&aLine[1],&aLine[tokenLength+1]);
+		// strip off carriage return or line feed
+		if (iscntrl(aLine[strlen(aLine)-1]))
+			{
+			aLine[strlen(aLine)-1] = '\0';
+			if (iscntrl(aLine[strlen(aLine)-1]))
+				aLine[strlen(aLine)-1] = '\0';
+			}
+		return testType;
+		}
+
+	token = strtok(NULL,delim);
+	if (testType == EEject || testType == EWaitDrive)
+		{
+		strcpy(aLine,token);
+
+		return testType;
+		}
+
+	if (testType == EFileReceive)
+		{
+		// The first parameter is the drive letter on the device to use
+		if (token != NULL)
+			aParam->minSize = token[0];
+		else
+			aParam->minSize = 'D';
+
+		token = strtok(NULL,delim);
+		}
+
+	while (token != NULL)
+		{
+		unsigned long ulParam = strtoul (token,NULL,0);
+		switch (paramCount)
+			{
+		case 0 :
+			aParam->minSize = (DWORD)ulParam;
+			break;
+
+		case 1 :
+			aParam->maxSize = (DWORD)ulParam;
+			break;
+
+		case 2 :
+			aParam->packetNumber = (DWORD)ulParam;
+			break;
+				
+		case 3 :
+			aParam->interfaceNumber = (BYTE)ulParam;
+			break;
+				
+		case 4:
+			aParam->alternateSetting = (BYTE)ulParam;
+			// skip outpipe for Xml IN
+			if (testType == EXml)
+				{
+				paramCount++;
+				}
+			break;
+
+		case 5:
+			aParam->outPipe = (BYTE)ulParam;
+			break;
+
+		case 6 :
+			aParam->inPipe = (BYTE)ulParam;
+			if (aParam->inPipe > 31)
+				{
+				if (testType == EReceiveOnly)
+					testType = ETransmitOnly;
+				if (testType == EFileReceive)
+					testType = EFileTransmit;
+				}
+			break;
+
+		case 7 :
+			aParam->repeat = (WORD)ulParam;
+			break;
+
+		case 8 :
+			aParam->host.displayRate = (WORD)ulParam;
+			break;
+
+		case 9 :
+			aParam->settingRepeat = (WORD)ulParam;
+			break;
+
+		case 10 :
+			aParam->beforeIndex = (BYTE)ulParam;
+			break;
+
+		case 11 :
+			aParam->afterIndex = (BYTE)ulParam;
+			break;
+
+		case 12 :
+			aParam->host.outRate = (DWORD)ulParam;
+			break;
+
+		case 13 :
+			aParam->host.inRate = (DWORD)ulParam;
+			break;
+			}
+
+		paramCount++;
+		token = strtok(NULL,delim);
+		}
+
+	return testType;
+	}
+
+static void ShowErrorText (DWORD errCode)
+	{
+	char textBuf[80];
+	
+	CUsbIo::ErrorText(textBuf,80,errCode);
+
+	PRINT_ALWAYS "%s"NL,textBuf); 
+	
+	gAbort = true;
+	}
+
+static DWORD OpenUsbDevice()
+	{
+	DWORD dwRC;
+	USB_DEVICE_DESCRIPTOR DevDesc;
+	// buffer used to store string descriptor
+	unsigned char StrDescBuffer[sizeof(USB_STRING_DESCRIPTOR)+512];
+	// pointer to USB string descriptor
+	USB_STRING_DESCRIPTOR* StrDesc;
+	DWORD StrDescSize;
+	WCHAR * WideString;
+	// helper variables  
+	bool deviceFound = false;
+	bool moreDevices = true;
+
+	if (gDevList)
+		{
+		CUsbIo::DestroyDeviceList(gDevList);
+		gDevList = NULL;
+		gDevNumber = -1;
+		gUsbDev.Close();	
+		}
+
+
+	// Enumerate attached USB devices supported by USBIO
+	gDevList = CUsbIo::CreateDeviceList(&gUsbioID);
+
+
+    if ( gDevList==NULL )
+		{
+		PRINT_ALWAYS "Unable to build a device list!"NL);
+	    return USBIO_ERR_FAILED;
+		}
+	
+    // open and query each device (max. 127)
+    for ( int i=0; i < 127 && !deviceFound && moreDevices; i++ )
+		{
+		StrDesc = NULL;
+
+		// open the device, using the global CUsbIo instance
+		dwRC = gUsbDev.Open(i,gDevList,&gUsbioID);
+		if ( dwRC != USBIO_ERR_SUCCESS )
+			{
+			// no more devices, or error leave loop
+			moreDevices = false;
+			break;
+			}
+
+		// we have found a device, query the device descriptor
+		dwRC = gUsbDev.GetDeviceDescriptor(&DevDesc);
+		if ( dwRC == USBIO_ERR_SUCCESS )
+			{
+			deviceFound = true;
+			// Check manufacturer string
+			if (strlen(gManufacturerName))
+				{
+			    // Does the device have a manufacturer?
+				if ( DevDesc.iManufacturer != NULL )
+					{
+					WideString = new(WCHAR [strlen(gManufacturerName)+1]);
+					for (unsigned int j = 0; j < strlen(gManufacturerName); j++)
+						{
+						WideString[j] = (WCHAR)gManufacturerName[j];
+						}
+					WideString[strlen(gManufacturerName)] = (WCHAR)0;
+					// query the corresponding string descriptor
+					StrDesc = (USB_STRING_DESCRIPTOR*)StrDescBuffer;
+					StrDescSize = sizeof(StrDescBuffer);
+					dwRC = gUsbDev.GetStringDescriptor(StrDesc,StrDescSize,DevDesc.iManufacturer);
+					if ( dwRC == USBIO_ERR_SUCCESS )
+						{
+						size_t stringSize = wcslen (WideString);
+						if (stringSize != (size_t)(StrDesc->bLength - 2)/2)
+							deviceFound = false;
+						else
+							{
+							if (wcsncmp (WideString,StrDesc->bString,stringSize) != 0)
+								deviceFound = false;
+							}
+ 						}
+					else
+						{
+							deviceFound = false;
+						}
+					delete WideString;
+					}
+				else
+					{
+					deviceFound = false;
+					}
+				}
+			
+			// check product string
+			if (strlen(gProductName))
+				{
+			    // Does the device have a manufacturer?
+				if ( DevDesc.iProduct !=0 )
+					{
+					WideString = new(WCHAR [strlen(gProductName)+1]);
+					for (unsigned int j = 0; j < strlen(gProductName); j++)
+						{
+						WideString[j] = (WCHAR)gProductName[j];
+						}
+					WideString[strlen(gProductName)] = (WCHAR)0;
+					// query the corresponding string descriptor
+					StrDesc = (USB_STRING_DESCRIPTOR*)StrDescBuffer;
+					StrDescSize = sizeof(StrDescBuffer);
+					dwRC = gUsbDev.GetStringDescriptor(StrDesc,StrDescSize,DevDesc.iProduct);
+					if ( dwRC == USBIO_ERR_SUCCESS )
+						{
+						size_t stringSize = wcslen (WideString);
+						if (stringSize != (size_t)(StrDesc->bLength - 2)/2)
+							deviceFound = false;
+						else
+							{
+							if (wcsncmp (WideString,StrDesc->bString,stringSize) != 0)
+								deviceFound = false;
+							}
+						}
+					else
+						{
+							deviceFound = false;
+						}
+					delete WideString;
+					}
+				else
+					{
+					deviceFound = false;
+					}
+				}
+
+			// check serial number string
+			if (strlen(gSerialNumber))
+				{
+			    // Does the device have a serial number?
+				if ( DevDesc.iSerialNumber !=0 )
+					{
+					WideString = new(WCHAR [strlen(gSerialNumber)+1]);
+					for (unsigned int j = 0; j < strlen(gSerialNumber); j++)
+						{
+						WideString[j] = (WCHAR)gSerialNumber[j];
+						}
+					WideString[strlen(gSerialNumber)] = (WCHAR)0;
+					// query the corresponding string descriptor
+					StrDesc = (USB_STRING_DESCRIPTOR*)StrDescBuffer;
+					StrDescSize = sizeof(StrDescBuffer);
+					dwRC = gUsbDev.GetStringDescriptor(StrDesc,StrDescSize,DevDesc.iSerialNumber);
+					if ( dwRC == USBIO_ERR_SUCCESS )
+						{
+						size_t stringSize = wcslen (WideString);
+						if (stringSize != (size_t)(StrDesc->bLength - 2)/2)
+							deviceFound = false;
+						else
+							{
+							if (wcsncmp (WideString,StrDesc->bString,stringSize) != 0)
+								deviceFound = false;
+							}
+						}
+					else
+						{
+							deviceFound = false;
+						}
+					delete WideString;
+					}
+				else
+					{
+					deviceFound = false;
+					}
+				}
+			if (deviceFound)
+				{
+				gDevNumber = i;
+				}
+			else
+				{
+				// close device
+				gUsbDev.Close();
+				}
+			}
+		else
+			{
+			// close device
+			gUsbDev.Close();
+			}
+		} // for 
+
+
+	if (dwRC == USBIO_ERR_VERSION_MISMATCH)
+		{
+		PRINT_ALWAYS "\n* Error: \"The API version reported by the TUSBWIN driver"NL \
+			   "*         does not match the expected version.\""NL); 
+		PRINT_ALWAYS "* The driver will need to be updated as follows:"NL); 
+		PRINT_ALWAYS "* 1. Connect the device to the PC & start T_USB,"NL \
+			   "* then find the USB device in the Windows Device Manager"NL \
+			   "* ('Control Panel'->'System'->'Hardware'->'Device Manager')."NL \
+			   "* Right click on the device name and choose 'Uninstall...'."NL); 
+		PRINT_ALWAYS "* 2. In c:\\winnt\\inf\\, find (by searching for \"Symbian\") and"NL \
+			   "* delete the *.INF file that was used to install the existing"NL \
+			   "* version of TUSBWIN.SYS. Make sure to also delete the"NL \
+			   "* precompiled version of that file (<samename>.PNF)."NL); 
+		PRINT_ALWAYS "* 3. In c:\\winnt\\system32\\drivers\\, delete the file TUSBWIN.SYS."NL);
+		PRINT_ALWAYS "* Then unplug & reconnect the USB device and, when prompted, install"NL \
+			   "* the new TUSBWIN.SYS driver using the .INF file from this distribution."NL \
+			   "* (All files can be found under e32test\\usb\t_usb_win\\bin_distribution\\.)"NL); 
+		}
+
+	return dwRC;
+	}
+
+static DWORD OpenSwitchDevice()
+	{
+	DWORD dwRC;
+
+	if (gDevList)
+		{
+		CUsbIo::DestroyDeviceList(gDevList);
+		gDevList = NULL;
+		gUsbDev.Close();	
+		}
+
+	// Enumerate attached USB devices supported by USBIO
+	gDevList = CUsbIo::CreateDeviceList(&gSwitchID);
+
+	if (gDevList)
+		{
+		// Open first device in list
+		dwRC = gUsbDev.Open(0, gDevList, &gSwitchID);
+
+		PRINT_IF_VERBOSE "\nCUsbIo::Open returned <0x%X>"NL, dwRC);
+		}
+	else
+		{
+		dwRC = USBIO_ERR_DEVICE_NOT_FOUND;
+		}
+
+	return dwRC;
+	}
+
+static void CloseUsbDevice()
+	{
+	// Close the device
+	gUsbDev.Close();
+	PRINT_IF_VERBOSE "CUsbIo::Close called"NL);
+	}
+
+static DWORD GetDeviceDescriptor()
+	{
+	DWORD dwRC;
+	USB_DEVICE_DESCRIPTOR DeviceDescriptor;
+
+	memset(&DeviceDescriptor, 0, sizeof(USB_DEVICE_DESCRIPTOR));
+
+	// Get device descriptor
+	dwRC = gUsbDev.GetDeviceDescriptor(&DeviceDescriptor);
+	PRINT_IF_VERBOSE "CUsbIo::GetDeviceDescriptor returned <0x%X>"NL, dwRC);
+
+	if (dwRC == USBIO_ERR_SUCCESS)
+		{
+		vendorId = DeviceDescriptor.idVendor;
+		productId = DeviceDescriptor.idProduct;
+		}
+
+	if (gVerboseMode && (dwRC == USBIO_ERR_SUCCESS))
+		{
+		PRINT_ALWAYS "\nDEVICE DESCRIPTOR:"NL
+			   "bLength = <%u>"NL
+			   "bDescriptorType = <%u>"NL
+			   "bcdUSB = <%u>"NL
+			   "bDeviceClass = <%u>"NL
+			   "bDeviceSubClass = <%u>"NL
+			   "bDeviceProtocol = <%u>"NL
+			   "bMaxPacketSize0 = <%u>"NL
+			   "idVendor = <%u>"NL
+			   "idProduct = <%u>"NL
+			   "bcdDevice = <%u>"NL
+			   "iManufacturer = <%u>"NL
+			   "iProduct = <%u>"NL
+			   "iSerialNumber = <%u>"NL
+			   "bNumConfigurations = <%u>\n"NL,
+			   DeviceDescriptor.bLength,
+			   DeviceDescriptor.bDescriptorType,
+			   DeviceDescriptor.bcdUSB,
+			   DeviceDescriptor.bDeviceClass,
+			   DeviceDescriptor.bDeviceSubClass,
+			   DeviceDescriptor.bDeviceProtocol,
+			   DeviceDescriptor.bMaxPacketSize0,
+			   DeviceDescriptor.idVendor,
+			   DeviceDescriptor.idProduct,
+			   DeviceDescriptor.bcdDevice,
+			   DeviceDescriptor.iManufacturer,
+			   DeviceDescriptor.iProduct,
+			   DeviceDescriptor.iSerialNumber,
+			   DeviceDescriptor.bNumConfigurations); 
+		}
+
+	if (gVerboseMode)
+	{
+		USBIO_BANDWIDTH_INFO bwInfo;
+
+		// Get bandwidth available
+		dwRC = gUsbDev.GetBandwidthInfo (&bwInfo);
+		if (dwRC == USBIO_ERR_SUCCESS)
+			{
+			PRINT_ALWAYS "Bandwidth total %d consumed %d"NL,bwInfo.TotalBandwidth,bwInfo.ConsumedBandwidth); 
+			}
+	}
+
+	return dwRC;
+	}
+
+
+static DWORD GetConfigurationDescriptor()
+	{
+	DWORD dwRC;
+	int ifListIndex = 0;
+
+	CHAR szBuffer[MAX_DESCRIPTOR_BUFFER_SIZE] = "";
+	USB_CONFIGURATION_DESCRIPTOR* pConfigDescriptor = NULL;
+	USB_INTERFACE_DESCRIPTOR* pInterfaceDescriptor = NULL;
+	USB_ENDPOINT_DESCRIPTOR* pEndpointDescriptor = NULL;
+
+	DWORD dwByteCount = MAX_DESCRIPTOR_BUFFER_SIZE;
+
+	memset(szBuffer, 0, sizeof(szBuffer));
+
+	memset (gEndpointMap, 0, sizeof(gEndpointMap));
+	memset (gPacketSizeMap, 0, sizeof(gPacketSizeMap));
+
+	// force a set interface 
+	memset (gAlternateSettings, 0, sizeof(gAlternateSettings));
+
+	// Get first configuration descriptor
+	dwRC = gUsbDev.GetConfigurationDescriptor((USB_CONFIGURATION_DESCRIPTOR*) szBuffer,
+											dwByteCount, 0);
+	PRINT_IF_VERBOSE "CUsbIo::GetConfigurationDescriptor returned <0x%X>"NL, dwRC);
+
+	USBIO_SET_CONFIGURATION SetConfig;
+
+	memset(&SetConfig, 0, sizeof(USBIO_SET_CONFIGURATION));
+
+	// Set the first configuration as active
+	SetConfig.ConfigurationIndex = 0;
+
+	if (dwRC == USBIO_ERR_SUCCESS)
+		{
+		USB_COMMON_DESCRIPTOR* Desc;
+		ULONG rl = dwByteCount;
+		ULONG ulDescLength = 0;
+		CHAR* data = szBuffer;
+		int interfaceNumber = 0;
+		int alternateSetting = 0;
+		int endpointIndex = 0;
+
+		while (rl > 0)
+			{
+			Desc = (USB_COMMON_DESCRIPTOR*) data;
+			ulDescLength = Desc->bLength;
+			if ((ulDescLength > rl) || (ulDescLength == 0))
+				{
+				PRINT_ALWAYS "Length remaining too short!"NL); 
+				rl = 0;
+				}
+			else
+				{
+				switch (Desc->bDescriptorType)
+					{
+				case USB_CONFIGURATION_DESCRIPTOR_TYPE:
+					pConfigDescriptor =
+						(USB_CONFIGURATION_DESCRIPTOR*) data;
+					PRINT_IF_VERBOSE "\nCONFIGURATION DESCRIPTOR:"NL
+							   "bLength = <%u>"NL
+							   "bDescriptorType = <%u>"NL
+							   "wTotalLength = <%u>"NL
+							   "bNumInterfaces = <%u>"NL
+							   "bConfigurationValue = <%u>"NL
+							   "iConfiguration = <%u>"NL
+							   "bmAttributes = <%u>"NL
+							   "MaxPower = <%u>"NL,
+							   pConfigDescriptor->bLength,
+							   pConfigDescriptor->bDescriptorType,
+							   pConfigDescriptor->wTotalLength,
+							   pConfigDescriptor->bNumInterfaces,
+							   pConfigDescriptor->bConfigurationValue,
+							   pConfigDescriptor->iConfiguration,
+							   pConfigDescriptor->bmAttributes,
+							   pConfigDescriptor->MaxPower); 
+					SetConfig.NbOfInterfaces = pConfigDescriptor->bNumInterfaces;
+					break;
+
+				case USB_INTERFACE_DESCRIPTOR_TYPE:
+					pInterfaceDescriptor = (USB_INTERFACE_DESCRIPTOR*) data;
+					interfaceNumber = pInterfaceDescriptor->bInterfaceNumber;
+					alternateSetting = pInterfaceDescriptor->bAlternateSetting;
+					endpointIndex = 0;
+					PRINT_IF_VERBOSE "\nINTERFACE DESCRIPTOR: "NL
+							   "bLength = <%u>"NL
+							   "bDescriptorType = <%u>"NL
+							   "bInterfaceNumber = <%u>"NL
+							   "bAlternateSetting = <%u>"NL
+							   "bNumEndpoints = <%u>"NL
+							   "bInterfaceClass = <%u>"NL
+							   "bInterfaceSubClass = <%u>"NL
+							   "bInterfaceProtocol = <%u>"NL
+							   "iInterface = <%u>"NL,
+							   pInterfaceDescriptor->bLength,
+							   pInterfaceDescriptor->bDescriptorType,
+							   pInterfaceDescriptor->bInterfaceNumber,
+							   pInterfaceDescriptor->bAlternateSetting,
+							   pInterfaceDescriptor->bNumEndpoints,
+							   pInterfaceDescriptor->bInterfaceClass,
+							   pInterfaceDescriptor->bInterfaceSubClass,
+							   pInterfaceDescriptor->bInterfaceProtocol,
+							   pInterfaceDescriptor->iInterface); 
+					if (alternateSetting == 0)
+						{
+						SetConfig.InterfaceList[ifListIndex].InterfaceIndex = interfaceNumber;
+						SetConfig.InterfaceList[ifListIndex++].MaximumTransferSize = KMaxTransferSize;
+						}
+					break;
+
+				case USB_ENDPOINT_DESCRIPTOR_TYPE:
+					pEndpointDescriptor =
+						(USB_ENDPOINT_DESCRIPTOR*) data;
+					gEndpointMap[interfaceNumber] [alternateSetting] [endpointIndex] = pEndpointDescriptor->bEndpointAddress;
+					gPacketSizeMap[interfaceNumber] [alternateSetting] [endpointIndex++] = pEndpointDescriptor->wMaxPacketSize;
+					PRINT_IF_VERBOSE "\nENDPOINT DESCRIPTOR: "NL
+							   "bLength = <%u>"NL
+							   "bDescriptorType = <%u>"NL
+							   "bEndpointAddress = <%X>"NL
+							   "bmAttributes = <%u>"NL
+							   "wMaxPacketSize = <%u>"NL
+							   "bInterval = <%u>"NL,
+							   pEndpointDescriptor->bLength,
+							   pEndpointDescriptor->bDescriptorType,
+							   pEndpointDescriptor->bEndpointAddress,
+							   pEndpointDescriptor->bmAttributes,
+							   pEndpointDescriptor->wMaxPacketSize,
+							   pEndpointDescriptor->bInterval); 
+					break;
+				default:
+					break;
+					}
+				}
+			data += ulDescLength;
+			rl -= ulDescLength;
+			}
+		}
+
+	dwRC = gUsbDev.SetConfiguration(&SetConfig);
+	PRINT_IF_VERBOSE "CUsbIo::SetConfiguration returned <0x%X>"NL, dwRC);
+
+		
+	PRINT_IF_VERBOSE ""NL);
+
+	return dwRC;
+	}
+
+
+static DWORD GetStringDescriptor()
+	{
+	DWORD dwRC;
+
+	CHAR szBuffer[MAX_DESCRIPTOR_BUFFER_SIZE] = "";
+	USB_STRING_DESCRIPTOR* pStringDescriptor = NULL;
+	DWORD dwByteCount = MAX_DESCRIPTOR_BUFFER_SIZE;
+
+	memset(szBuffer, 0, sizeof(szBuffer));
+
+	// Get string descriptor
+	dwRC = gUsbDev.GetStringDescriptor((USB_STRING_DESCRIPTOR*) szBuffer,
+										dwByteCount, 1, 0);
+	PRINT_IF_VERBOSE "CUsbIo::GetStringDescriptor returned <0x%X>"NL, dwRC);
+
+	if (dwRC == USBIO_ERR_SUCCESS)
+		{
+		pStringDescriptor = (USB_STRING_DESCRIPTOR*) szBuffer;
+		PRINT_IF_VERBOSE "\nSTRING DESCRIPTOR:"NL
+				   "bLength = <%u>"NL
+				   "bDescriptorType = <%u>"NL
+				   "bString = <",							// output continues below!
+				   pStringDescriptor->bLength,
+				   pStringDescriptor->bDescriptorType); 
+		INT i = 0;
+		CHAR* Ptr = szBuffer;
+		for (i = 2, Ptr += 2;
+			 i < pStringDescriptor->bLength;
+			 i += 2, Ptr += 2)
+			{
+			PRINT_IF_VERBOSE "%c", *Ptr);
+			}
+		PRINT_IF_VERBOSE ">\n"NL);
+		}
+
+	return dwRC;
+	}
+
+
+static DWORD GetConfigurationInfo()
+	{
+	DWORD dwRC;
+
+	USBIO_CONFIGURATION_INFO ConfigInfo;
+	USHORT i = 0;
+
+	memset(&ConfigInfo, 0, sizeof(USBIO_CONFIGURATION_INFO));
+
+	dwRC = gUsbDev.GetConfigurationInfo(&ConfigInfo);
+	PRINT_IF_VERBOSE "CUsbIo::GetConfigurationInfo returned <0x%X>"NL, dwRC);
+
+	if (dwRC == USBIO_ERR_SUCCESS)
+		{
+		PRINT_IF_VERBOSE "\nCONFIGURATION INFO:"NL
+				   "NbOfInterfaces = <%lu>"NL
+				   "NbOfPipes = <%lu>"NL,
+				   ConfigInfo.NbOfInterfaces,
+				   ConfigInfo.NbOfPipes); 
+		for (i = 0; i < ConfigInfo.NbOfInterfaces; i++)
+			{
+			PRINT_IF_VERBOSE "\nINTERFACE <%u>:"NL, i + 1); 
+			PRINT_IF_VERBOSE "InterfaceNumber = <%u>"NL
+					   "AlternateSetting = <%u>"NL
+					   "Class = <%u>"NL
+					   "SubClass = <%u>"NL
+					   "Protocol = <%u>"NL
+					   "NumberOfPipes = <%u>"NL
+					   "reserved1 = <%u>"NL
+					   "reserved2 = <%u>"NL,
+					   ConfigInfo.InterfaceInfo[i].InterfaceNumber,
+					   ConfigInfo.InterfaceInfo[i].AlternateSetting,
+					   ConfigInfo.InterfaceInfo[i].Class,
+					   ConfigInfo.InterfaceInfo[i].SubClass,
+					   ConfigInfo.InterfaceInfo[i].Protocol,
+					   ConfigInfo.InterfaceInfo[i].NumberOfPipes,
+					   ConfigInfo.InterfaceInfo[i].reserved1,
+					   ConfigInfo.InterfaceInfo[i].reserved2); 
+			}
+		for (i = 0; i < ConfigInfo.NbOfPipes; i++)
+			{
+			PRINT_IF_VERBOSE ""NL);
+			PRINT_IF_VERBOSE "PIPE <%u>"NL, i + 1); 
+			PRINT_IF_VERBOSE "PipeType = <%d>"NL
+					   "MaximumTransferSize = <%lu>"NL
+					   "MaximumPacketSize = <%u>"NL
+					   "EndpointAddress = <%X>"NL
+					   "Interval = <%u>"NL
+					   "InterfaceNumber = <%u>"NL
+					   "reserved1 = <%u>"NL
+					   "reserved2 = <%u>"NL
+					   "reserved3 = <%u>"NL,
+					   ConfigInfo.PipeInfo[i].PipeType,
+					   ConfigInfo.PipeInfo[i].MaximumTransferSize,
+					   ConfigInfo.PipeInfo[i].MaximumPacketSize,
+					   ConfigInfo.PipeInfo[i].EndpointAddress,
+					   ConfigInfo.PipeInfo[i].Interval,
+					   ConfigInfo.PipeInfo[i].InterfaceNumber,
+					   ConfigInfo.PipeInfo[i].reserved1,
+					   ConfigInfo.PipeInfo[i].reserved2,
+					   ConfigInfo.PipeInfo[i].reserved3); 
+			}
+		}
+	PRINT_IF_VERBOSE ""NL);
+
+	return dwRC;
+	}
+
+
+static DWORD OpenPipes(CUsbIoPipe * inPipePtr, CUsbIoPipe * outPipePtr, TestParamPtr tpPtr)
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+	UCHAR epAddress;
+
+	// Create the bulk OUT pipe
+	if (outPipePtr)
+		{
+		epAddress = gEndpointMap[tpPtr->interfaceNumber][tpPtr->alternateSetting][tpPtr->outPipe-1];
+		dwRC = outPipePtr->Bind(gDevNumber, epAddress, gDevList, &gUsbioID);
+
+		PRINT_IF_VERBOSE "CUsbIoPipe::Bind OUT address <0x%X> returned <0x%X>"NL, epAddress,dwRC);
+		
+		if (dwRC == USBIO_ERR_SUCCESS)
+			{
+			// Set up for time statistics on OUT pipe
+			dwRC = outPipePtr->SetupPipeStatistics(1000L);
+			PRINT_IF_VERBOSE "CUsbIoPipe::SetupPipeStatistics OUT returned <0x%X>"NL, dwRC);
+			}
+		else
+			ShowErrorText(dwRC);
+		}
+
+	if (inPipePtr && dwRC == USBIO_ERR_SUCCESS)
+		{
+		// Create the IN pipe
+		epAddress = gEndpointMap[tpPtr->interfaceNumber][tpPtr->alternateSetting][tpPtr->inPipe-1];
+		dwRC = inPipePtr->Bind(gDevNumber, epAddress, gDevList, &gUsbioID);
+
+		PRINT_IF_VERBOSE "CUsbIoPipe::Bind IN address <0x%X> returned <0x%X>"NL, epAddress,dwRC);
+
+		if (dwRC == USBIO_ERR_SUCCESS)
+			{
+			// Set up for time statistics on IN pipe
+			dwRC = inPipePtr->SetupPipeStatistics(1000L);
+			PRINT_IF_VERBOSE "CUsbIoPipe::SetupPipeStatistics IN returned <0x%X>"NL, dwRC);
+			}
+		else
+			ShowErrorText(dwRC);
+
+		}
+
+	PRINT_IF_VERBOSE ""NL);
+
+	return dwRC;
+	}
+
+
+static DWORD ClosePipes(CUsbIoPipe * inPipePtr, CUsbIoPipe * outPipePtr)
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+
+	// Close down the OUT pipe
+	if (outPipePtr)
+		{
+		dwRC = outPipePtr->Unbind();
+		PRINT_IF_VERBOSE "CUsbIoPipe::Unbind OUT returned <0x%X>"NL, dwRC);
+		}
+
+	if (inPipePtr && dwRC == USBIO_ERR_SUCCESS)
+		{
+		// Close down the IN pipe
+		dwRC = inPipePtr->Unbind();
+		PRINT_IF_VERBOSE "CUsbIoPipe::Unbind IN returned <0x%X>"NL, dwRC);
+		}
+
+	return dwRC;
+	}
+
+static void SendLogFile()
+	{
+	logStream = fopen (gLogfileName, "rb");
+	if (logStream == NULL)
+		{
+		return;
+		}
+
+	char logBuffer[KHostLogFileSize];
+	CUsbIoPipe* outPipePtr = new CUsbIoPipe();
+	CUsbIoBuf bufPtr((VOID*) logBuffer, KHostLogFileSize);			// the data buffer
+	int bytesRead;
+
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+	UCHAR epAddress;
+
+	// Create the bulk OUT pipe
+	epAddress = gEndpointMap[0][0][1];
+	dwRC = outPipePtr->Bind(gDevNumber, epAddress, gDevList, &gUsbioID);
+	if (dwRC != USBIO_ERR_SUCCESS)
+		{
+		return;
+		}
+
+	bytesRead = fread ( logBuffer, sizeof(char), KHostLogFileSize, logStream );
+	if (bytesRead > 0)
+		{
+		bufPtr.NumberOfBytesToTransfer = bytesRead;
+		outPipePtr->Write(&bufPtr);
+		dwRC = outPipePtr->WaitForCompletion(&bufPtr, gReadWriteTimeOut);
+		if (dwRC != USBIO_ERR_SUCCESS)
+			{
+			return;
+			}
+		}
+
+
+	bufPtr.NumberOfBytesToTransfer = 0;
+	outPipePtr->Write(&bufPtr);
+	dwRC = outPipePtr->WaitForCompletion(&bufPtr, gReadWriteTimeOut);
+
+	return;
+	}
+
+static DWORD ReceiveCounter()
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+
+	DWORD bytes_read = sizeof(__int64);
+
+	// Here we (hope to) read an 8 byte packet containing a counter value.
+	PRINT_NOLOG "* Waiting for T_USB counter packet to arrive..."); 
+
+	__int64 counter;
+
+	USBIO_CLASS_OR_VENDOR_REQUEST request;
+	request.Flags = USBIO_SHORT_TRANSFER_OK;
+	request.Type = RequestTypeClass;
+	request.Recipient = RecipientDevice;
+	request.RequestTypeReservedBits = 0;
+	request.Request = ETestIdleCounter;
+	request.Value = gDeviceIdleCount < 0 ? 0 : 1;
+	request.Index = 0;
+
+
+	dwRC = gUsbDev.ClassOrVendorInRequest(&counter,bytes_read,&request);
+
+	PRINT_NOLOG " done."NL); 
+	if (dwRC != USBIO_ERR_SUCCESS)
+		{
+		PRINT_ALWAYS "\n* Error: CUsbIoPipe::Read (version)"); 
+		ShowErrorText(dwRC);
+		return dwRC;
+		}
+	
+	if (gDeviceIdleCount < 0)
+		{
+		// Initialise counter
+		gDeviceIdleCount = counter;
+		_ftime(&gHostTime);
+		PRINT_TIME " * Start Device Idle Counter"NL); 
+		}
+	else
+		{
+		// if the counter is zero then the tests havent completed on the device
+		while (counter < gDeviceIdleCount)
+			{
+			Delay(WAIT_TEST_COMPLETE);
+			dwRC = gUsbDev.ClassOrVendorInRequest(&counter,bytes_read,&request);
+			if (dwRC != USBIO_ERR_SUCCESS)
+				{
+				PRINT_ALWAYS "\n* Error: CUsbIoPipe::Read (version)"); 
+				ShowErrorText(dwRC);
+				return dwRC;
+				}
+			}
+		__int64 diff = counter - gDeviceIdleCount;
+		struct _timeb tmp = gHostTime;
+		_ftime(&gHostTime);
+		gDeviceIdleCount = counter;
+		int ms = (gHostTime.time - tmp.time) * 1000 + gHostTime.millitm - tmp.millitm;
+		PRINT_TIME " * Stop Device Idle Counter: %I64d increments in %dms (%dinc/ms)"NL,
+			diff, ms, diff / ms);
+		}
+
+	return dwRC;
+	}
+
+static DWORD ReceiveVersion()
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+
+	// Here we (hope to) read an 8 byte packet containing the T_USB version.
+	PRINT_NOLOG "* Waiting for T_USB version packet to arrive..."); 
+
+	BYTE versionData[VERSION_DATAIN_SIZE];
+
+	// The first 3 bytes are interpreted as a version.
+	DWORD bytes_read = VERSION_DATAIN_SIZE;
+
+	USBIO_CLASS_OR_VENDOR_REQUEST request;
+	request.Flags = USBIO_SHORT_TRANSFER_OK;
+	request.Type = RequestTypeClass;
+	request.Recipient = RecipientDevice;
+	request.RequestTypeReservedBits = 0;
+	request.Request = EVersion;
+	request.Value = 0;
+	request.Index = 0;
+
+
+	dwRC = gUsbDev.ClassOrVendorInRequest(&versionData[0],bytes_read,&request);
+
+	PRINT_NOLOG " done."NL); 
+	if (dwRC != USBIO_ERR_SUCCESS)
+		{
+		PRINT_ALWAYS "\n* Error: CUsbIoPipe::Read (version)"); 
+		ShowErrorText(dwRC);
+		return dwRC;
+		}
+	if (versionData[0] < KTusbVersionMajor || 
+		(versionData[0] == KTusbVersionMajor && versionData[1] < KTusbVersionMinor) || 
+		(versionData[0] == KTusbVersionMajor && versionData[1] == KTusbVersionMinor && versionData[2] < KTusbVersionMicro)) 
+		{
+		PRINT_ALWAYS "* Inadequate version of T_USB: %d.%d.%d (we need at least %d.%d.%d)"NL,
+			   versionData[0],versionData[1],versionData[2],KTusbVersionMajor,KTusbVersionMinor,KTusbVersionMicro); 
+		dwRC = USBIO_ERR_FAILED;
+		return dwRC;
+		}
+	PRINT_NOLOG "* Suitable version of T_USB_DEVICE found: %d.%d.%d."NL, versionData[0],versionData[1],versionData[2]); 
+
+	char * scriptFilePtr;
+	scriptFilePtr = strstr((char *)&versionData[3],"/script=");
+
+	if (scriptFilePtr != NULL)
+		{
+		* scriptFilePtr = '\0';
+		scriptFilePtr += 8;
+		PRINT_ALWAYS "Script file name %s"NL,scriptFilePtr);
+		if (strstr(gScriptFileName,scriptFilePtr) == NULL)
+			{
+			dwRC = ERR_SCRIPT_NAME;
+			}
+		}
+
+	PRINT_ALWAYS "Device configuration file  %s"NL,&versionData[3]);
+
+	return dwRC;
+	}
+
+
+static DWORD SendVersion()
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+
+	// Here we send an 8 byte packet containing T_USB_WIN's + USBIO's versions.
+	PRINT_NOLOG "* Sending our version packet to T_USB..."); 
+
+	BYTE versionData[VERSION_DATAOUT_SIZE];
+	versionData[0] = VERSION_MAJOR;
+	versionData[1] = VERSION_MINOR;
+	versionData[2] = VERSION_MICRO;
+	versionData[3] = USBIO_VERSION_MAJOR;
+	versionData[4] = USBIO_VERSION_MINOR;
+
+	USBIO_CLASS_OR_VENDOR_REQUEST request;
+	request.Flags = USBIO_SHORT_TRANSFER_OK;
+	request.Type = RequestTypeClass;
+	request.Recipient = RecipientDevice;
+	request.RequestTypeReservedBits = 0;
+	request.Request = EVersion;
+	request.Value = 0;
+	request.Index = 0;
+
+	DWORD data_bytes = VERSION_DATAOUT_SIZE;
+	dwRC = gUsbDev.ClassOrVendorOutRequest(&versionData[0],data_bytes,&request);
+	PRINT_NOLOG " done."NL); 
+	if (dwRC != USBIO_ERR_SUCCESS)
+		{
+		PRINT_ALWAYS "\n* Error: CUsbIoPipe::Write (version)"); 
+		ShowErrorText(dwRC);
+		}
+
+	return dwRC;
+	}
+
+
+static DWORD ExchangeVersions()
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+
+
+	dwRC = SendVersion();
+	if (dwRC != USBIO_ERR_SUCCESS)
+		return dwRC;
+	dwRC = ReceiveVersion();
+
+	return dwRC;
+	}
+
+
+static DWORD ChangeInterfaceSetting(BYTE aInterfaceNumber, BYTE aAlternateSetting)
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+	
+	if (gAlternateSettings[aInterfaceNumber] != aAlternateSetting)
+		{
+		gAlternateSettings[aInterfaceNumber] = aAlternateSetting;
+		USBIO_INTERFACE_SETTING ifSetting = {aInterfaceNumber,aAlternateSetting,4096};
+		dwRC = gUsbDev.SetInterface(&ifSetting);
+		if (dwRC == USBIO_ERR_SUCCESS)
+			{
+			PRINT_NOLOG "Set Interface %d to Alternate Setting %d"NL,aInterfaceNumber,aAlternateSetting); 
+			}
+		else
+			{
+			PRINT_ALWAYS "\n* Error: Setting Interface %d Alternate Setting %d",aInterfaceNumber,aAlternateSetting); 
+			ShowErrorText(dwRC);
+			}
+		}
+	
+	return dwRC;
+	}
+
+static DWORD ReadData(CUsbIoPipe * inPipePtr,CUsbIoBuf* bufPtr, TestParamPtr tpPtr)
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+	UCHAR epAddress = gEndpointMap[tpPtr->interfaceNumber][tpPtr->alternateSetting][tpPtr->inPipe-1];
+
+	// We have to setup a read for at least one byte in order to get
+	// the host to issue IN tokens for our zero-byte read:
+	inPipePtr->Read(bufPtr);
+	dwRC = inPipePtr->WaitForCompletion(bufPtr, gReadWriteTimeOut);
+
+	if (dwRC != USBIO_ERR_SUCCESS)
+		{
+		PRINT_ALWAYS "\n* Error: EP Number %d Address <0x%X> CUsbIoPipe::Read %d bytes",tpPtr->inPipe,epAddress, bufPtr->NumberOfBytesToTransfer); 
+		ShowErrorText(dwRC);
+		}
+	else
+		{
+		if (bufPtr->BytesTransferred != bufPtr->NumberOfBytesToTransfer)
+			{
+			DWORD pktNum = *(DWORD *)bufPtr->Buffer();
+			PRINT_ALWAYS "* EP Address <0x%X> Read more/less bytes (%d) than expected (%d) packet number 0x%x"NL, 
+				   epAddress, bufPtr->BytesTransferred,bufPtr->NumberOfBytesToTransfer,pktNum); 
+			dwRC = USBIO_ERR_FAILED;
+			}
+		else
+			{
+			PRINT_IF_VERBOSE "* EP Address <0x%X> Read %d bytes."NL, epAddress,bufPtr->BytesTransferred);
+			}
+		}
+
+	return dwRC;
+	}
+
+
+static DWORD WriteData(CUsbIoPipe * outPipePtr,CUsbIoBuf* bufPtr, TestParamPtr tpPtr)
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+	UCHAR epAddress = gEndpointMap[tpPtr->interfaceNumber][tpPtr->alternateSetting][tpPtr->outPipe-1];
+	
+	outPipePtr->Write(bufPtr);
+	dwRC = outPipePtr->WaitForCompletion(bufPtr, gReadWriteTimeOut);
+
+	if (dwRC == USBIO_ERR_SUCCESS)
+		{
+		PRINT_IF_VERBOSE "* EP Address <0x%X> Wrote %d bytes."NL, epAddress, bufPtr->BytesTransferred);
+		}
+	else
+		{
+		PRINT_ALWAYS "\n* Error: EP Number %d Address <0x%X> CUsbIoPipe::Write %d bytes",tpPtr->outPipe,epAddress, bufPtr->NumberOfBytesToTransfer); 
+		ShowErrorText(dwRC);
+		}
+
+	return dwRC;
+	}
+
+
+static DWORD CheckAndPrintStats(CUsbIoPipe * inPipePtr, CUsbIoPipe * outPipePtr, BYTE testIndex, DWORD loopCount, DWORD * averageInRate, DWORD * averageOutRate, DWORD inRate = 0, DWORD outRate = 0)
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+	USBIO_PIPE_STATISTICS pipeStats;
+	
+	PRINT_NOLOG "*** Test Index %d Iteration %d ***"NL,testIndex,loopCount); 
+	if (outPipePtr)
+		{
+		dwRC = outPipePtr->QueryPipeStatistics (&pipeStats);
+	
+		if (dwRC == USBIO_ERR_SUCCESS)
+			{
+			__int64 byteCount = (((__int64)pipeStats.BytesTransferred_H)<<32) + pipeStats.BytesTransferred_L;
+			PRINT_NOLOG "OUT %I64u bytes  Succeeded %lu Failed %lu Rate %lu bytes/sec."NL,
+				byteCount,pipeStats.RequestsSucceeded,pipeStats.RequestsFailed,pipeStats.AverageRate); 
+			
+			if (pipeStats.RequestsFailed)
+				{
+				PRINT_ALWAYS "%d OUT Requests Failed"NL,pipeStats.RequestsFailed);
+				dwRC = USBIO_ERR_FAILED;
+				}
+			* averageOutRate = pipeStats.AverageRate;
+			if (pipeStats.AverageRate < outRate)
+				{
+				PRINT_ALWAYS "Failure Average OUT Rate %lu < Minimum Rate %lu"NL,pipeStats.AverageRate,outRate);
+				dwRC = USBIO_ERR_FAILED;
+				}
+			}
+		else
+			{
+			PRINT_ALWAYS "\n* Error: CUsbIoPipe::Query OUT Pipe Statistics"); 
+			ShowErrorText(dwRC);
+			}
+
+		}
+
+	if (inPipePtr && dwRC == USBIO_ERR_SUCCESS)
+		{
+		dwRC = inPipePtr->QueryPipeStatistics (&pipeStats);
+	
+		if (dwRC == USBIO_ERR_SUCCESS)
+			{
+			__int64 byteCount = (((__int64)pipeStats.BytesTransferred_H)<<32) + pipeStats.BytesTransferred_L;
+			PRINT_NOLOG "IN %I64u bytes  Succeeded %lu Failed %lu Rate %lu bytes/sec."NL,
+				byteCount,pipeStats.RequestsSucceeded,pipeStats.RequestsFailed,pipeStats.AverageRate); 
+
+			if (pipeStats.RequestsFailed)
+				{
+				PRINT_ALWAYS "%d IN Requests Failed"NL,pipeStats.RequestsFailed);
+				dwRC = USBIO_ERR_FAILED;
+				}
+			* averageInRate = pipeStats.AverageRate;
+			if (pipeStats.AverageRate < inRate)
+				{
+				PRINT_ALWAYS "Failure Average IN Rate %lu < Minimum Rate %lu"NL,pipeStats.AverageRate,inRate);
+				dwRC = USBIO_ERR_FAILED;
+				}
+
+			}
+		else
+			{
+			PRINT_ALWAYS "\n* Error: CUsbIoPipe::Query IN Pipe Statistics"); 
+			ShowErrorText(dwRC);
+			}
+		}
+
+	return dwRC;
+	}
+
+
+UINT LoopTransfer( LPVOID pParam )
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+
+	TestParamType testParams = *(TestParamPtr)pParam;
+
+	PRINT_NOLOG "* Thread %d Loop Transfers -- reading & writing alternately."NL,testParams.host.index); 
+
+	ResetEvent (gThreadEndedEvents[testParams.host.index]);
+	SetEvent (gThreadStartedEvent);
+
+	CUsbIoPipe* inPipePtr = new CUsbIoPipe();
+	CUsbIoPipe* outPipePtr = new CUsbIoPipe();
+
+	BYTE* DataPtr = new BYTE[testParams.maxSize];
+	CUsbIoBuf buf((VOID*) DataPtr, testParams.maxSize);			// the data buffer
+
+	BYTE* WaitSettingDataPtr = new BYTE[WAIT_SETTING_BUFFER_SIZE];
+	CUsbIoBuf bufWaitSetting((VOID*) WaitSettingDataPtr, WAIT_SETTING_BUFFER_SIZE);			// another data buffer
+	bufWaitSetting.NumberOfBytesToTransfer = WAIT_SETTING_BUFFER_SIZE;
+
+	if (!testParams.settingRepeat)
+		{
+		OpenPipes (inPipePtr,outPipePtr,&testParams);
+		}
+
+	DWORD averageInRate;
+	DWORD averageOutRate;
+	int repeatCount = 0;
+	buf.NumberOfBytesToTransfer = testParams.minSize;
+	while ((dwRC == USBIO_ERR_SUCCESS) && !gFinishAllTests && !gAbort && (!testParams.repeat || repeatCount < testParams.repeat))
+		{
+		if (testParams.settingRepeat)
+			{
+			if ((repeatCount % testParams.settingRepeat) == 0)
+				{
+				if (repeatCount)
+					{
+					// Wait for this extra read to ensure that ok to change interface setting
+					inPipePtr->Read(&bufWaitSetting);
+					dwRC = inPipePtr->WaitForCompletion(&bufWaitSetting, WAIT_SETTING_TIMEOUT);
+					// Not necessary if different interfaces used as setting interface closes pipes,
+					// but if the same interface is used then prevents errors when opening pipes
+					ClosePipes (inPipePtr,outPipePtr);
+					PRINT_IF_VERBOSE "Signal for thread %d"NL,testParams.host.index);
+					SetEvent (gThreadWaitEvents[testParams.host.index]);
+					}
+				
+				PRINT_IF_VERBOSE "Wait for thread %d"NL,testParams.beforeIndex);
+				dwRC = WaitForSingleObject (gThreadWaitEvents[testParams.beforeIndex],WAIT_BEFORETHREAD_TIMEOUT);
+				if (dwRC != WAIT_OBJECT_0)
+					{
+					PRINT_ALWAYS "Error waiting for alternate setting before thread"NL);
+					return 0;
+					}
+				dwRC = ChangeInterfaceSetting (testParams.interfaceNumber,testParams.alternateSetting);
+				OpenPipes (inPipePtr,outPipePtr,&testParams);
+				}
+			}
+
+		dwRC = ReadData(inPipePtr,&buf,&testParams);
+		if (dwRC == USBIO_ERR_SUCCESS)
+			{
+			// Now we send the received data back to the client.
+			dwRC = WriteData(outPipePtr,&buf,&testParams);
+			}
+
+		repeatCount++;
+		if (dwRC == USBIO_ERR_SUCCESS)
+			{
+			if (repeatCount == testParams.repeat)
+				{
+				dwRC = CheckAndPrintStats(inPipePtr,outPipePtr,testParams.host.index,repeatCount,&averageInRate,&averageOutRate,testParams.host.inRate,testParams.host.outRate);
+				}
+			else
+				{
+				if (testParams.host.displayRate && (repeatCount % testParams.host.displayRate) == 0)
+					{
+					dwRC = CheckAndPrintStats(inPipePtr,outPipePtr,testParams.host.index,repeatCount,&averageInRate,&averageOutRate);
+					}
+				}
+			}
+
+		if (buf.NumberOfBytesToTransfer == testParams.maxSize)
+			{
+			buf.NumberOfBytesToTransfer = testParams.minSize;
+			}
+		else
+			{
+			buf.NumberOfBytesToTransfer++;
+			}
+		}
+
+
+	Delay (WAIT_TEST_COMPLETE);
+	USBIO_CLASS_OR_VENDOR_REQUEST request;
+	request.Flags = USBIO_SHORT_TRANSFER_OK;
+	request.Type = RequestTypeClass;
+	request.Recipient = RecipientDevice;
+	request.RequestTypeReservedBits = 0;
+	request.Request = ETestResult;
+	request.Index = testParams.host.index;
+	request.Value = (dwRC == USBIO_ERR_SUCCESS) ? true : false;
+	DWORD dataBytes = 0;
+	dwRC = gUsbDev.ClassOrVendorOutRequest(NULL,dataBytes,&request);
+
+	PRINT_TIME "Test Index %d IN rate %lu OUT rate %lu Loop %s  ",testParams.host.index,averageInRate,averageOutRate,request.Value ? "Passed" : "Failed"); 
+
+	dwRC = ClosePipes (inPipePtr,outPipePtr);
+	delete DataPtr;
+	delete inPipePtr;
+	delete outPipePtr;
+
+	if (request.Value)
+		{
+		if (testParams.host.signalNextSettingThread)
+			{
+			PRINT_IF_VERBOSE "Signal for thread %d"NL,testParams.host.index);
+			SetEvent (gThreadWaitEvents[testParams.host.index]);
+			}
+		}
+	else
+		{	
+		gAbort = true;
+		}
+
+	SetEvent (gThreadEndedEvents[testParams.host.index]);
+
+	return 0;
+	}
+
+
+UINT ReceiveOnlyTransfer(LPVOID pParam)
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+	BYTE* DataPtr;										// the read/write buffer
+
+	TestParamType testParams = *(TestParamPtr)pParam;
+
+	PRINT_NOLOG "* Receive-only transfers (IN)."NL); 
+
+	ResetEvent (gThreadEndedEvents[testParams.host.index]);
+	SetEvent (gThreadStartedEvent);
+
+	CUsbIoPipe * inPipePtr = new CUsbIoPipe();
+
+	DataPtr = new BYTE[testParams.maxSize];
+	CUsbIoBuf buf((VOID*) DataPtr, testParams.maxSize);			// the data buffer
+
+	if (!testParams.settingRepeat)
+		{
+		OpenPipes (inPipePtr,NULL,&testParams);
+		}
+
+	buf.NumberOfBytesToTransfer = testParams.maxSize;
+	DWORD averageInRate;
+	DWORD averageOutRate;
+	int repeatCount = 0;
+	DWORD expected = testParams.packetNumber;
+	DWORD pktNum = 0;
+	int saveTimeout = gReadWriteTimeOut;
+	gReadWriteTimeOut = FIRSTFILEREAD_TIMEOUT;
+	while ((dwRC == USBIO_ERR_SUCCESS) && !gFinishAllTests && !gAbort && (!testParams.repeat || repeatCount < testParams.repeat))
+		{
+		if (testParams.settingRepeat)
+			{
+			if ((repeatCount % testParams.settingRepeat) == 0)
+				{
+				if (repeatCount)
+					{
+					// Wait to ensure that ok to change interface setting
+					Delay (WAIT_SETTING_DELAY);
+					// Not necessary if different interfaces used as setting interface closes pipes,
+					// but if the same interface is used then prevents errors when opening pipes
+					ClosePipes (inPipePtr,NULL);
+					PRINT_IF_VERBOSE "Signal for thread %d"NL,testParams.host.index);
+					SetEvent (gThreadWaitEvents[testParams.host.index]);
+					}
+				
+				PRINT_IF_VERBOSE "Wait for thread %d"NL,testParams.beforeIndex);
+				dwRC = WaitForSingleObject (gThreadWaitEvents[testParams.beforeIndex],WAIT_BEFORETHREAD_TIMEOUT);
+				if (dwRC != WAIT_OBJECT_0)
+					{
+					PRINT_ALWAYS "Error waiting for alternate setting before thread"NL); 
+					return 0;
+					}
+				dwRC = ChangeInterfaceSetting(testParams.interfaceNumber,testParams.alternateSetting);
+				OpenPipes (inPipePtr,NULL,&testParams);
+				}
+			}
+
+		// Then we read the data bytes
+		dwRC = ReadData(inPipePtr,&buf,&testParams);
+		if (gDeviceIdleCount < 0)
+			{
+			gReadWriteTimeOut = saveTimeout;
+			ReceiveCounter();
+			}
+		pktNum = *(DWORD *)buf.Buffer();
+		if (pktNum != expected)
+			{
+			PRINT_ALWAYS "\n* Error: rcv'd wrong pkt number: 0x%x (expected: 0x%x)"NL, pktNum, expected); 
+			expected = pktNum;
+			}
+		expected++;
+		// Finally, sometimes we print some statistics
+		repeatCount++;
+		if (dwRC == USBIO_ERR_SUCCESS)
+			{
+			if (repeatCount == testParams.repeat)
+				{
+				dwRC = CheckAndPrintStats(inPipePtr,NULL,testParams.host.index,repeatCount,&averageInRate,&averageOutRate,testParams.host.inRate,testParams.host.outRate);
+				}
+			else
+				{
+				if (testParams.host.displayRate && (repeatCount % testParams.host.displayRate) == 0)
+					{
+					dwRC = CheckAndPrintStats(inPipePtr,NULL,testParams.host.index,repeatCount,&averageInRate,&averageOutRate);
+					}
+				}
+			}
+		else
+			{
+			PRINT_ALWAYS "\n* Error: Receive-only transfer::read"); 
+			ShowErrorText(dwRC);
+			}
+		}
+
+	Delay (WAIT_TEST_COMPLETE);
+	USBIO_CLASS_OR_VENDOR_REQUEST request;
+	request.Flags = USBIO_SHORT_TRANSFER_OK;
+	request.Type = RequestTypeClass;
+	request.Recipient = RecipientDevice;
+	request.RequestTypeReservedBits = 0;
+	request.Request = ETestResult;
+	request.Index = testParams.host.index;
+	request.Value = (dwRC == USBIO_ERR_SUCCESS) ? true : false;
+	DWORD dataBytes = 0;
+	dwRC = gUsbDev.ClassOrVendorOutRequest(NULL,dataBytes,&request);
+
+	PRINT_TIME "Test Index %d IN rate %lu Receieve-Only %s  ",testParams.host.index,averageInRate,request.Value ? "Passed" : "Failed"); 
+	
+	dwRC = ClosePipes (inPipePtr,NULL);
+
+	delete DataPtr;
+	delete inPipePtr;
+
+	if (request.Value)
+		{
+		if (testParams.host.signalNextSettingThread)
+			{
+			PRINT_IF_VERBOSE "Signal for thread %d"NL,testParams.host.index);
+			SetEvent (gThreadWaitEvents[testParams.host.index]);
+			}
+		}
+	else
+		{	
+		gAbort = true;
+		}
+
+	SetEvent (gThreadEndedEvents[testParams.host.index]);
+
+	return 0;
+	}
+
+
+UINT TransmitOnlyTransfer(LPVOID pParam)
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+	BYTE* DataPtr;										// the read/write buffer
+
+	PRINT_NOLOG "* Transmit-only transfers (OUT)."NL); 
+
+	TestParamType testParams = *(TestParamPtr)pParam;
+
+	ResetEvent (gThreadEndedEvents[testParams.host.index]);
+	SetEvent (gThreadStartedEvent);
+
+	CUsbIoPipe * outPipePtr = new CUsbIoPipe();
+
+	DataPtr = new BYTE[testParams.maxSize];
+	CUsbIoBuf buf((VOID*) DataPtr, testParams.maxSize);			// the data buffer
+
+	BYTE* WaitSettingDataPtr = new BYTE[WAIT_SETTING_BUFFER_SIZE];
+	CUsbIoBuf bufWaitSetting((VOID*) WaitSettingDataPtr, WAIT_SETTING_BUFFER_SIZE);			// another data buffer
+	bufWaitSetting.NumberOfBytesToTransfer = WAIT_SETTING_BUFFER_SIZE;
+
+	if (!testParams.settingRepeat)
+		{
+		OpenPipes (NULL,outPipePtr,&testParams);
+		}
+
+	buf.NumberOfBytesToTransfer = testParams.maxSize;
+	DWORD averageInRate;
+	DWORD averageOutRate;
+	int repeatCount = 0;
+	DWORD pktNum = testParams.packetNumber;
+	while ((dwRC == USBIO_ERR_SUCCESS) && !gFinishAllTests && !gAbort && (!testParams.repeat || repeatCount < testParams.repeat))
+		{
+		if (testParams.settingRepeat)
+			{
+			if ((repeatCount % testParams.settingRepeat) == 0)
+				{
+				if (repeatCount)
+					{
+					// Wait to ensure that ok to change interface setting
+					Delay (WAIT_SETTING_DELAY);
+					// Not necessary if different interfaces used as setting interface closes pipes,
+					// but if the same interface is used then prevents errors when opening pipes
+					ClosePipes (NULL,outPipePtr);
+					PRINT_IF_VERBOSE "Signal for thread %d"NL,testParams.host.index);
+					SetEvent (gThreadWaitEvents[testParams.host.index]);
+					}
+				
+				PRINT_IF_VERBOSE "Wait for thread %d"NL,testParams.beforeIndex);
+				dwRC = WaitForSingleObject (gThreadWaitEvents[testParams.beforeIndex],WAIT_BEFORETHREAD_TIMEOUT);
+				if (dwRC != WAIT_OBJECT_0)
+					{
+					PRINT_ALWAYS "Error waiting for alternate setting before thread"NL); 
+					return 0;
+					}
+				dwRC = ChangeInterfaceSetting (testParams.interfaceNumber,testParams.alternateSetting);
+				OpenPipes (NULL,outPipePtr,&testParams);
+				}
+			}
+		
+		
+		// Then we write 'Length' bytes
+		*(DWORD *)buf.Buffer() = pktNum++;
+		dwRC = WriteData(outPipePtr,&buf,&testParams);
+
+		repeatCount++;
+		// Finally, sometimes we print some statistics
+		if (dwRC == USBIO_ERR_SUCCESS)
+			{
+			if (repeatCount == testParams.repeat)
+				{
+				dwRC = CheckAndPrintStats(NULL,outPipePtr,testParams.host.index,repeatCount,&averageInRate,&averageOutRate,testParams.host.inRate,testParams.host.outRate);
+				}
+			else
+				{
+				if (testParams.host.displayRate && (repeatCount % testParams.host.displayRate) == 0)
+					{
+					dwRC = CheckAndPrintStats(NULL,outPipePtr,testParams.host.index,repeatCount,&averageInRate,&averageOutRate);
+					}
+				}
+			}
+		else
+			{
+			PRINT_ALWAYS "\n* Error: Transmit-only transfer::write"); 
+			ShowErrorText(dwRC);
+			}
+		}
+
+	Delay (WAIT_TEST_COMPLETE);
+	USBIO_CLASS_OR_VENDOR_REQUEST request;
+	request.Flags = USBIO_SHORT_TRANSFER_OK;
+	request.Type = RequestTypeClass;
+	request.Recipient = RecipientDevice;
+	request.RequestTypeReservedBits = 0;
+	request.Request = ETestResult;
+	request.Index = testParams.host.index;
+	request.Value = (dwRC == USBIO_ERR_SUCCESS) ? true : false;
+	DWORD dataBytes = 0;
+	dwRC = gUsbDev.ClassOrVendorOutRequest(NULL,dataBytes,&request);
+
+	PRINT_TIME "Test Index %d OUT rate %lu Transmit Only %s  ",testParams.host.index,averageOutRate,request.Value ? "Passed" : "Failed"); 
+
+	ClosePipes (NULL,outPipePtr);
+	delete DataPtr;
+	delete outPipePtr;
+
+	if (request.Value)
+		{
+		if (testParams.host.signalNextSettingThread)
+			{
+			PRINT_IF_VERBOSE "Signal for thread %d"NL,testParams.host.index);
+			SetEvent (gThreadWaitEvents[testParams.host.index]);
+			}
+		}
+	else
+		{	
+		gAbort = true;
+		}
+
+	SetEvent (gThreadEndedEvents[testParams.host.index]);
+
+	return 0;
+	}
+
+
+static BOOL CreateEvents()
+	{
+    // Create a manual-reset event object.
+	// Any thread sets this to signaled when it has started. 
+    gThreadStartedEvent = CreateEvent( 
+        NULL,         // no security attributes
+        TRUE,         // manual-reset event
+        FALSE,         // initial state is non-signaled
+        "ThreadStarted"  // object name
+        ); 
+    if (gThreadStartedEvent == NULL)
+		{ 
+		PRINT_ALWAYS "Error creating thread started event"NL); 
+        // error exit
+		return FALSE;
+		}
+    // Create a manual-reset event object. 
+    // Set to signaled by CT_usb_winDlg when the device is connected. 
+    gDeviceConnectEvent = CreateEvent( 
+        NULL,         // no security attributes
+        TRUE,         // manual-reset event
+        FALSE,         // initial state is non-signaled
+        "DeviceConnected"  // object name
+        ); 
+	if (gDeviceConnectEvent == NULL)
+		{ 
+		PRINT_ALWAYS "Error creating device connected event"NL); 
+        // error exit
+		return FALSE;
+		}
+    // Create a manual-reset event object. 
+    // Set to nonsignaled by CT_usb_winDlg when the device is disconnected. 
+    gDeviceDisconnectEvent = CreateEvent( 
+        NULL,         // no security attributes
+        TRUE,         // manual-reset event
+        FALSE,         // initial state is non-signaled
+        "DeviceDisconnected"  // object name
+        ); 
+   if (gDeviceDisconnectEvent == NULL)
+		{ 
+		PRINT_ALWAYS "Error creating device disconnected event"NL); 
+        // error exit
+		return FALSE;
+		}
+
+	for (int i = 0; i < MAX_THREADS; i++)
+		{	
+	    // Create manual-reset event object for each thread. Any thread sets 
+		// this to nonsignaled when it has started. 
+	    gThreadEndedEvents[i] = CreateEvent( 
+		    NULL,         // no security attributes
+			TRUE,         // manual-reset event
+			FALSE,        // initial state is non-signaled
+			""			  // object name
+			); 
+	    if (gThreadEndedEvents[i] == NULL)
+			{ 
+			PRINT_ALWAYS "Error creating thread %d ended event"NL,i); 
+			// error exit
+			return FALSE;
+			}
+	    // Create auto-reset event object for each thread. Any thread sets 
+		// this to nonsignaled when it has started. 
+	    gThreadWaitEvents[i] = CreateEvent( 
+		    NULL,         // no security attributes
+			FALSE,        // automatically-reset event
+			FALSE,        // initial state is non-signaled
+			""			  // object name
+			); 
+	    if (gThreadEndedEvents[i] == NULL)
+			{ 
+			PRINT_ALWAYS "Error creating thread %d ended event"NL,i); 
+			// error exit
+			return FALSE;
+			}
+		}
+	return TRUE;
+	}
+
+
+static BOOL GetDeviceInfo()
+	{
+	DWORD dwRC;
+
+	dwRC = GetDeviceDescriptor();
+	if (dwRC == USBIO_ERR_SUCCESS)
+		{
+		dwRC = GetConfigurationDescriptor();
+		}
+	if (dwRC == USBIO_ERR_SUCCESS)
+		{
+		dwRC = GetStringDescriptor();
+		}
+	if (dwRC == USBIO_ERR_SUCCESS)
+		{
+		// In order to give the USB device-side program (t_usb_device)
+		// enough time after getting configured to carry out
+		// some device tests, we wait here for a short while
+		// before proceeding:
+		Delay(DEVICE_CONFIG_DELAY);
+		dwRC = GetConfigurationInfo();
+		}
+	if (dwRC == USBIO_ERR_SUCCESS)
+		{
+		dwRC = ExchangeVersions();
+		}
+	if (dwRC != USBIO_ERR_SUCCESS)
+		{
+		if (dwRC == ERR_SCRIPT_NAME)
+			PRINT_ALWAYS "*** Warning Script Name Mismatch "NL);
+		else
+			ShowErrorText(dwRC);
+		return FALSE;
+		}
+
+	return TRUE;
+	}
+
+UINT DoTests(LPVOID pParam)
+	{
+	DWORD dwRC = USBIO_ERR_SUCCESS;
+	bool firstSetting = true;
+	BYTE threadIndex = 0;
+	BYTE lastSettingThreadIndex = 0;
+	TestParamType testParams;
+	USBIO_CLASS_OR_VENDOR_REQUEST request;
+	DWORD dataBytes;
+	FILE* inStream;
+	char line[256];
+	long repeatPosition = -1;
+	long repeatCount = -1;
+	UsbcvControl * usbcv;
+	bool hostTestFail = false;
+	bool mstoreDriveReady = false;
+	DWORD waitConnectTime = 0;
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+
+	if (!CreateEvents())
+		{
+		return FALSE;
+		}
+	if( (inStream  = fopen( gScriptFileName, "r" )) == NULL )
+		{
+		PRINT_ALWAYS  "The test script file %s was not opened"NL,gScriptFileName ); 
+		return FALSE;
+		}
+	else
+		{
+		PRINT_IF_VERBOSE  "The test script file %s opened"NL,gScriptFileName);   
+		}
+
+	PRINT_ALWAYS "Script file  %s"NL,gScriptFileName);
+
+	// set up the EP 0 request structure	
+	request.Flags = USBIO_SHORT_TRANSFER_OK;
+	request.Type = RequestTypeClass;
+	request.Recipient = RecipientDevice;
+	request.RequestTypeReservedBits = 0;
+
+	gTransferMode = EComment;
+
+	PRINT_TIME "Tests Started  ");
+
+	/* Cycle until end of file reached: */
+	while( !feof( inStream ) && !gAbort)
+		{
+		testParams.host.index = threadIndex;
+
+		// for loopback tests 
+		if (gTransferMode < EComment)
+			{
+			if (testParams.settingRepeat == 0)
+				{
+				dwRC = ChangeInterfaceSetting (testParams.interfaceNumber,testParams.alternateSetting);
+				}
+			request.Request = ETestParam;
+			request.Index = testParams.host.index;
+			if (firstSetting && testParams.settingRepeat)
+				{
+				request.Index |= FIRST_SETTING_THREAD_MASK;
+				PRINT_IF_VERBOSE "Signal for thread %d"NL,testParams.beforeIndex);
+				lastSettingThreadIndex = testParams.beforeIndex;
+				SetEvent (gThreadWaitEvents[lastSettingThreadIndex]);
+				firstSetting = false;
+				}
+			if (testParams.settingRepeat && threadIndex == lastSettingThreadIndex)
+				{
+				request.Index |= LAST_SETTING_THREAD_MASK;
+				}
+			dataBytes = sizeof(testParams) - sizeof(TestParamHostType);
+			dwRC = gUsbDev.ClassOrVendorOutRequest(&testParams,dataBytes,&request);
+			if (dwRC != USBIO_ERR_SUCCESS)
+				{
+				PRINT_ALWAYS "\n* Error: CUsbIo::ClassOrVendorOutRequest "); 
+				ShowErrorText(dwRC);
+				}
+			}
+
+		testParams.host.signalNextSettingThread = (testParams.settingRepeat && threadIndex != lastSettingThreadIndex);
+		switch (gTransferMode)
+			{
+			case EXml:
+				break;
+
+			case ELoop:
+			case ECompare:
+				if (gDeviceIdleCount < 0)
+					{
+					ReceiveCounter();
+					}
+				AfxBeginThread (LoopTransfer, &testParams);
+				break;
+
+			case EReceiveOnly:
+			case EFileReceive:
+				if (gDeviceIdleCount < 0 && gTransferMode != EFileReceive)
+					{
+					ReceiveCounter();
+					}
+				AfxBeginThread (ReceiveOnlyTransfer, &testParams);
+				break;
+
+			case ETransmitOnly:
+			case EFileTransmit:
+				if (gDeviceIdleCount < 0)
+					{
+					ReceiveCounter();
+					}
+				AfxBeginThread (TransmitOnlyTransfer, &testParams);
+				break;
+
+			case EConnect:
+				// ensure that connect and disconnect events are reset
+				ResetEvent (gDeviceConnectEvent);
+				ResetEvent (gDeviceDisconnectEvent);
+				request.Request = ETestConnect;
+				request.Value = (USHORT)testParams.minSize;
+				dataBytes = 0;
+				dwRC = gUsbDev.ClassOrVendorOutRequest(NULL,dataBytes,&request);
+				if (dwRC != USBIO_ERR_SUCCESS)
+					{
+					PRINT_ALWAYS "\n* Error: CUsbIo::ClassOrVendorOutRequest "); 
+					ShowErrorText(dwRC);
+					}
+				break;
+
+			case EDisconnect:
+				if (gUsbDev.IsOpen())
+					{
+					CloseUsbDevice();
+					}
+				break;
+
+			case EComment:
+				break;
+
+			case EWait:
+				if (testParams.host.index > 0)
+					{
+					PRINT_ALWAYS "Waiting for %d tests to complete\n",testParams.host.index); 
+					dwRC = WaitForMultipleObjects (testParams.host.index,gThreadEndedEvents,TRUE,testParams.minSize);
+					if (dwRC == WAIT_TIMEOUT)
+						{
+						PRINT_ALWAYS "Timeout on waiting for test completion\n");
+						bool anyTestNotCompleted = false;
+						for (int i = 0; i < testParams.host.index; i++)
+							{
+							dwRC = WaitForSingleObject(gThreadEndedEvents[i],0);
+							if (dwRC != WAIT_OBJECT_0)
+								{
+								anyTestNotCompleted = true;
+								PRINT_ALWAYS "Thread %d not completed\n",i);
+								}
+							}
+						if (anyTestNotCompleted)
+							{
+							gFinishAllTests = true;
+							dwRC = WaitForMultipleObjects (testParams.host.index,gThreadEndedEvents,TRUE,INFINITE);
+							gFinishAllTests = false;
+							gAbort = true;
+							}
+						}
+					if (dwRC < WAIT_OBJECT_0 || dwRC >= (WAIT_OBJECT_0 + testParams.host.index))
+						{
+						PRINT_ALWAYS "Error waiting for all threads to complete"NL); 
+						gAbort = true;
+						gFinishAllTests = true;
+						}
+					ReceiveCounter();
+					gDeviceIdleCount = -1;
+					PRINT_TIME "Test Group Complete  "); 
+					}
+				threadIndex = 0;
+				firstSetting = true;
+				break;
+
+			case EFail:
+				request.Request = ETestFail;
+				dataBytes = 0;
+				dwRC = gUsbDev.ClassOrVendorOutRequest(NULL,dataBytes,&request);
+				if (dwRC != USBIO_ERR_SUCCESS)
+					{
+					PRINT_ALWAYS "\n* Error: CUsbIo::ClassOrVendorOutRequest "); 
+					ShowErrorText(dwRC);
+					}
+				gAbort = true;
+				break;
+
+			case ESwitch:
+				PRINT_NOLOG "Switching USB to %d"NL,testParams.minSize); 
+				dwRC = OpenSwitchDevice ();
+				if (dwRC == USBIO_ERR_SUCCESS)
+					{
+					dwRC = GetConfigurationDescriptor();		
+					}
+				if (dwRC == USBIO_ERR_SUCCESS)
+					{
+					// Delay to allow the switch to take place
+					Delay (BEFORE_SWITCH_DELAY);
+
+					request.Recipient = RecipientInterface;
+					request.Request = SWITCH_REQUEST;
+					request.Index = SWITCH_INDEX;
+					request.Value = SWITCH_VALUE;
+					BYTE switchData[] = {SWITCH_DATA0,((BYTE)testParams.minSize << 4) | SWITCH_DATA1};
+					DWORD dataBytes = sizeof(switchData);
+					dwRC = gUsbDev.ClassOrVendorOutRequest(&switchData[0],dataBytes,&request);
+					request.Recipient = RecipientDevice;					
+					CloseUsbDevice();
+
+					// Delay to allow the switch to take place
+					Delay (AFTER_SWITCH_DELAY);
+					}
+				if (dwRC != USBIO_ERR_SUCCESS)
+					{
+					PRINT_ALWAYS "\n* Error: GetConfigurationDescriptor or CUsbIo::ClassOrVendorOutRequest "); 				
+					gAbort = true;
+					}
+				break;
+
+			case EWaitConnect:
+				PRINT_NOLOG "Waiting for device connection"NL); 
+				waitConnectTime = 0;
+				while (!gAbort && !gUsbDev.IsOpen())
+					{
+					ResetEvent (gDeviceConnectEvent);
+					ResetEvent (gDeviceDisconnectEvent);
+					dwRC = OpenUsbDevice();
+					if (dwRC == USBIO_ERR_SUCCESS)
+						{
+						if (!GetDeviceInfo())
+							{
+							dwRC = USBIO_ERR_FAILED;
+							PRINT_ALWAYS "\n* Error: GetDeviceInfo"NL);
+							gAbort = TRUE;
+							hostTestFail = FALSE;
+							}
+						}
+					else
+						{
+						dwRC = WaitForSingleObject (gDeviceConnectEvent,CONNECT_RESCAN_TIMEOUT);
+						if ((dwRC == WAIT_ABANDONED) || (dwRC == WAIT_FAILED))
+							{
+							PRINT_ALWAYS "Error waiting for device connection"NL); 
+							gAbort = TRUE;
+							hostTestFail = FALSE;
+							}
+						if (dwRC == WAIT_TIMEOUT)
+							{
+							waitConnectTime += CONNECT_RESCAN_TIMEOUT;
+							if (testParams.minSize && waitConnectTime >= testParams.minSize)
+								{
+								PRINT_ALWAYS "Timeout for device connection"NL); 
+								gAbort = TRUE;
+								hostTestFail = FALSE;
+								}
+							else
+								{
+							    ZeroMemory( &si, sizeof(si) );
+								si.cb = sizeof(si);
+								ZeroMemory( &pi, sizeof(pi) );
+
+								dwRC = CreateProcess (RESCAN_APPNAME,RESCAN_CMDLINE,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
+								if (dwRC < 0)
+									PRINT_ALWAYS "Error %d running Devcon Rescan"NL,dwRC); 
+								}
+							}
+						}
+					}
+				if (hostTestFail)
+					{
+					// try to send a test fail to the device
+					request.Request = ETestResult;
+					request.Index = HOST_ERROR_INDEX;
+					request.Value = false;
+					dataBytes = 0;
+					dwRC = gUsbDev.ClassOrVendorOutRequest(NULL,dataBytes,&request);
+					if (dwRC != USBIO_ERR_SUCCESS)
+						{
+						PRINT_ALWAYS "\n* Error: CUsbIo::ClassOrVendorOutRequest "); 
+						ShowErrorText(dwRC);
+						}
+					gAbort = true;
+					}
+				break;
+			
+			case EWaitDisconnect:
+				PRINT_NOLOG "Waiting for device disconnection"NL); 
+				dwRC = WaitForSingleObject (gDeviceDisconnectEvent,testParams.minSize);
+				if (dwRC != WAIT_OBJECT_0)
+					{
+					PRINT_ALWAYS "Error waiting for device disconnection"NL); 
+					gAbort = true;
+					}
+				// ensure that connect and disconnect events are reset
+				ResetEvent (gDeviceConnectEvent);
+				ResetEvent (gDeviceDisconnectEvent);
+				break;
+
+			case EDo:
+				repeatPosition = ftell(inStream);
+				repeatCount = 0;
+				PRINT_IF_VERBOSE "Repeating from file position %ld"NL,repeatPosition); 
+				break;
+
+			case ERepeat:
+				if (repeatPosition <= 0)
+					PRINT_ALWAYS "No REPEAT in file"NL);
+				else
+					{
+					if (repeatCount++ < (long)testParams.minSize)
+						{
+						if (!fseek( inStream, repeatPosition, SEEK_SET))
+							PRINT_ALWAYS "Repeat Iteration %d"NL,repeatCount);
+						else
+							PRINT_ALWAYS "ERROR: Repeat Iteration %d"NL,repeatCount);
+						}
+					}
+				break;
+
+			case EUsbcv:
+				CloseUsbDevice();
+				PRINT_TIME "USBCV Test Started  ");
+				usbcv = new UsbcvControl();
+				dwRC = usbcv->Create();
+				if (dwRC == USBIO_ERR_SUCCESS)
+					{
+					dwRC = usbcv->TestAllDevices(vendorId,productId, productIdMS);
+					}
+				delete usbcv;
+				if (dwRC == USBIO_ERR_SUCCESS)
+					{
+					PRINT_TIME "USBCV Test Complete - %s  ",gAbort ? "Failed" : "Passed");
+					}
+				else
+					{
+					gAbort = true;
+					PRINT_ALWAYS "Error in Running USBCV\n");
+					}
+				if (gAbort)
+					{
+					// allow further tests to carry on until waitConnect
+					hostTestFail = true;
+					gAbort = false;
+					}
+				// ensure that connect and disconnect events are reset
+				ResetEvent (gDeviceConnectEvent);
+				ResetEvent (gDeviceDisconnectEvent);
+				break;
+
+			case EMassStorage:
+				mstoreDriveReady = false;
+				ResetEvent (gDeviceConnectEvent);
+				ResetEvent (gDeviceDisconnectEvent);
+				PRINT_TIME "Mass Storage Test Started  ");
+				request.Request = ETestMassStorage;
+				request.Value = (USHORT)testParams.minSize;			// product id
+				productIdMS = (USHORT)testParams.minSize;
+				dataBytes = 0;
+				dwRC = gUsbDev.ClassOrVendorOutRequest(NULL,dataBytes,&request);
+				if (dwRC != USBIO_ERR_SUCCESS)
+					{
+					PRINT_ALWAYS "\n* Error: CUsbIo::ClassOrVendorOutRequest "); 
+					ShowErrorText(dwRC);
+					}
+				CloseUsbDevice();
+				if (gAbort)
+					{
+					// allow further tests to carry on until waitConnect
+					hostTestFail = true;
+					gAbort = false;
+					}
+				break;
+
+			case EEject:
+				if (mstoreDriveReady)
+					{
+					PRINT_TIME "Mass Storage Test Completed  ");
+					if (strlen(line) == 0)
+						gAbort = !EjectVolume(' ');
+					else
+						gAbort = !EjectVolume(line[0]);
+					PRINT_TIME "Mass Storage Test Complete - %s  ",gAbort || hostTestFail ? "Failed" : "Passed");
+					if (gAbort)
+						{
+						// allow further tests to carry on until waitConnect
+						hostTestFail = true;
+						gAbort = false;
+						}
+					}
+				break;
+
+			case EPerl:
+				if (mstoreDriveReady)
+					{
+					PRINT_TIME "Starting Perl Script %s ",line);
+					if (strlen(gLogfileName))
+						{
+						strcat (line," --logfile=");
+						strcat (line,gLogfileName);
+						fclose (logStream);
+						}
+					dwRC = PerlScript (line);
+					if (strlen(gLogfileName))
+						{
+						logStream = fopen (gLogfileName, "a+");
+						}
+					PRINT_TIME "Perl Script Complete - %s  ",dwRC == 0 ? "Passed" : "Failed");
+					if (dwRC != 0 || gAbort)
+						{
+						ShowErrorText (dwRC);
+						// allow further tests to carry on until waitConnect
+						hostTestFail = true;
+						gAbort = false;
+						}
+					}
+				break;
+
+			case EWaitDrive:
+				if (!hostTestFail)
+					{
+					PRINT_TIME "Waiting for removable drive - %c  ",line[0]);
+					gAbort = !WaitDriveReady(line[0]);
+					PRINT_TIME "Waiting completed - %s  ",gAbort ? "Failed" : "Successfully");
+					if (gAbort)
+						{
+						// allow further tests to carry on until waitConnect
+						hostTestFail = true;
+						gAbort = false;
+						}
+					else
+						{
+						mstoreDriveReady = true;
+						}
+					}
+				break;
+
+			default:
+				gAbort = true;
+				break;
+			}
+		if (!gAbort && gTransferMode < EComment)
+			{
+			dwRC = WaitForSingleObject (gThreadStartedEvent,1500);
+			if (dwRC != WAIT_OBJECT_0)
+				{
+				PRINT_ALWAYS "Error waiting for thread to become ready"NL); 
+				gAbort = true;
+				}
+			threadIndex++;
+			dwRC = ResetEvent (gThreadStartedEvent);
+			}
+		if (!gAbort)
+			{
+			if( fgets( line, 100, inStream ) != NULL)
+				{
+				gTransferMode = ParseTestLine (line,&testParams,&request);
+				}
+			}
+		}
+
+	if (gAbort)
+		{
+		PRINT_TIME "Tests Not Completed - Failed ");
+		}
+	else
+		{
+		request.Request = EStop;
+		request.Value = (logStream == NULL) ? false : true;
+		dataBytes = 0;
+		dwRC = gUsbDev.ClassOrVendorOutRequest(NULL,dataBytes,&request);
+		if (dwRC != USBIO_ERR_SUCCESS)
+			{
+			PRINT_ALWAYS "\n* Error: CUsbIo::ClassOrVendorOutRequest "); 
+			ShowErrorText(dwRC);
+			}
+
+		PRINT_TIME "Tests Completed - All Passed  ");
+		}
+
+
+	PRINT_ALWAYS NL);
+
+	if (logStream != NULL)
+		{
+		fclose (logStream);
+		if (!gAbort)
+			{
+			SendLogFile();
+			}
+		}
+
+	PRINT_NOLOG "Waiting for device disconnection"NL); 
+	dwRC = WaitForSingleObject (gDeviceDisconnectEvent,WAITDISCONNECT_TIMEOUT);
+	if (dwRC != WAIT_OBJECT_0 && !gAbort)
+		{
+		PRINT_ALWAYS "Error waiting for device disconnection"NL); 
+		}
+
+	CloseUsbDevice();
+
+	if (!gKeepOpen)
+		{
+		ExitProcess(0);
+		}
+
+	return TRUE;
+	}
+
+// --eof