kerneltest/e32test/win32/usbrflct/usbrflct.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 29 743008598095
parent 0 a41df078684a
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// 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\win32\usbrflct\usbrflct.cpp
// Win32 USB test program USBRFLCT: performs I/O with a device running the
// Symbian OS test program T_USB, using the generic USB device driver USBIO.
// === Includes ===
// 
//

#include <windows.h>
#include <stdio.h>
#include <iostream.h>
#include <time.h>

#include "usbio.h"											// USBIO Dev Kit
#include "usbiopipe.h"										// ditto


// === Global Defines ===

// The version of this program
#define VERSION_MAJOR  1
#ifdef VERSION_MINOR
# undef VERSION_MINOR						  // VERSION_MINOR sometimes yields funny compiler warning (c4005)
#endif
#define VERSION_MINOR  6
#ifdef VERSION_MICRO
# undef VERSION_MICRO						  // ditto
#endif
#define VERSION_MICRO  0

// The version of the USBIO driver this program is compiled against
#define USBIO_VERSION_MAJOR  2
#define USBIO_VERSION_MINOR  41

#define MAX_DESCRIPTOR_BUFFER_SIZE 2047

// === Global Vars ===

// Our own private GUID (also used in the .inf file as 'DriverUserInterfaceGuid')
// {55606403-E62D-4707-9F56-40D48C6736D0}
static const GUID g_UsbioID =
	{0x55606403, 0xe62d, 0x4707, {0x9f, 0x56, 0x40, 0xd4, 0x8c, 0x67, 0x36, 0xd0}};

// Handle for USB device list
static HDEVINFO g_DevList = NULL;

// USBIO supported device
static CUsbIo g_UsbDev;
static CUsbIoPipe g_BulkOutPipe;
static CUsbIoPipe g_BulkInPipe;

// 2 Bulk endpoints
static UCHAR g_ucBulkOutEndpoint = 0;
static UCHAR g_ucBulkInEndpoint = 0;

// Read/write buffer
static const DWORD KBufferSize = 1024 * 1024;
static const DWORD KPreambleLength = 8;
static BYTE Data[KBufferSize];								// the read/write buffer
static DWORD Length;										// Length of a transfer
static time_t T_0;											// the starting time

static CUsbIoBuf g_Buf((VOID*) Data, KBufferSize);		// the data buffer
static CUsbIoBuf g_ZlpBuf (NULL, 0);					// the data buffer

static DWORD dwRC = USBIO_ERR_SUCCESS;						// global error indicator

enum
	{
	ELoop,
	ELoopDebug,
	EReceiveOnly,
	ETransmitOnly
	};

static int TransferMode = ELoop;
static bool VerboseMode = false;
static bool ZlpMode = false;

static unsigned int maxOutPacketSize = 0;

// The version of T_USB we require (at least)
static const DWORD KTusbVersion = 20070524;

// After how many iterations to update the CRT:
static const int KLoopModeDisplayUpdate = 1024;
// After how many iterations (= MBytes) to update the CRT:
static const int KUniModeDisplayUpdate = 10;

// Helper #defines

#define PRINT_IF_VERBOSE(string) \
		do { \
		if (VerboseMode) \
			{ \
			printf(string); \
			} \
		} while (0)

#define PRINT_IF_VERBOSE1(string, a) \
		do { \
		if (VerboseMode) \
			{ \
			printf(string, a); \
			} \
		} while (0)


// === Functions ===

//
// Process the command line arguments, printing a helpful message
// if none are supplied.
//
static int ProcessCmdLine(int argc, char* argv[])
	{
	char help_text[] =
		"* Syntax: usbrflct [options]\n"
		"* Options:\n"
		"* /[r|t]    receive|transmit only; default is to loop\n"
		"* /z        zero length packet at end of a transmission\n"
		"*	    (only if last packet is full length)\n"
		"* /l        loop mode with stats printed for every iteration\n"
		"* /v        verbose driver & program output\n"
		"* /[h|?]    displays this help text\n"
		"\n";

	for (int i = 1; i < argc; i++)
		{
		strupr(argv[i]);
		if ((argv[i][0] == '-') || (argv[i][0] == '/'))
			{
			switch (argv[i][1])
				{
			case 'R':
			case 'r':
				TransferMode = EReceiveOnly;
				break;
			case 'T':
			case 't':
				TransferMode = ETransmitOnly;
				break;
			case 'L':
			case 'l':
				TransferMode = ELoopDebug;
				break;
			case 'V':
			case 'v':
				VerboseMode = true;
				break;
			case 'Z':
			case 'z':
				ZlpMode = true;
				break;
			case '?':
			case 'H':
			case 'h':
				cout << help_text;
				return -1;
			default:
				cout << "* Invalid argument: " << argv[i] << "\n";
				cout << help_text;
				return -1;
				}
			}
		}
	return 0;
	}


static void OpenUsbDevice()
	{
	CUsbIo::DestroyDeviceList(g_DevList);

	// Enumerate attached USB devices supported by USBIO
	g_DevList = CUsbIo::CreateDeviceList(&g_UsbioID);

	// Open first device in list
	dwRC = g_UsbDev.Open(0, g_DevList, &g_UsbioID);

	PRINT_IF_VERBOSE1("\nCUsbIo::Open returned <0x%X>\n", dwRC);

	if (dwRC == USBIO_ERR_VERSION_MISMATCH)
		{
		printf("\n* Error: \"The API version reported by the USBRFLCT driver\n" \
			   "*         does not match the expected version.\"\n");
		printf("* The driver will need to be updated as follows:\n");
		printf("* 1. Connect the device to the PC & start T_USB,\n" \
			   "* then find the USB device in the Windows Device Manager\n" \
			   "* ('Control Panel'->'System'->'Hardware'->'Device Manager').\n" \
			   "* Right click on the device name and choose 'Uninstall...'.\n");
		printf("* 2. In c:\\winnt\\inf\\, find (by searching for \"Symbian\") and\n" \
			   "* delete the *.INF file that was used to install the existing\n" \
			   "* version of USBRFLCT.SYS. Make sure to also delete the\n" \
			   "* precompiled version of that file (<samename>.PNF).\n");
		printf("* 3. In c:\\winnt\\system32\\drivers\\, delete the file USBRFLCT.SYS.\n");
		printf("* Then unplug & reconnect the USB device and, when prompted, install\n" \
			   "* the new USBRFLCT.SYS driver using the .INF file from this distribution.\n" \
			   "* (All files can be found under e32test\\win32\\usbrflct_distribution\\.)\n");
		}
	}


static void CloseUsbDevice()
	{
	// Close the device
	g_UsbDev.Close();
	PRINT_IF_VERBOSE("CUsbIo::Close called\n");
	}


static void GetDeviceDescriptor()
	{
	USB_DEVICE_DESCRIPTOR DeviceDescriptor;

	memset(&DeviceDescriptor, 0, sizeof(USB_DEVICE_DESCRIPTOR));

	// Get device descriptor
	dwRC = g_UsbDev.GetDeviceDescriptor(&DeviceDescriptor);
	PRINT_IF_VERBOSE1("CUsbIo::GetDeviceDescriptor returned <0x%X>\n", dwRC);

	if (VerboseMode && (dwRC == USBIO_ERR_SUCCESS))
		{
		printf("\nDEVICE DESCRIPTOR:\n"
			   "bLength = <%u>\n"
			   "bDescriptorType = <%u>\n"
			   "bcdUSB = <%u>\n"
			   "bDeviceClass = <%u>\n"
			   "bDeviceSubClass = <%u>\n"
			   "bDeviceProtocol = <%u>\n"
			   "bMaxPacketSize0 = <%u>\n"
			   "idVendor = <%u>\n"
			   "idProduct = <%u>\n"
			   "bcdDevice = <%u>\n"
			   "iManufacturer = <%u>\n"
			   "iProduct = <%u>\n"
			   "iSerialNumber = <%u>\n"
			   "bNumConfigurations = <%u>\n\n",
			   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);
		}
	}


static void GetConfigurationDescriptor()
	{
	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));

	// Get first configuration descriptor
	dwRC =
		g_UsbDev.GetConfigurationDescriptor((USB_CONFIGURATION_DESCRIPTOR*) szBuffer,
											dwByteCount, 0);
	PRINT_IF_VERBOSE1("CUsbIo::GetConfigurationDescriptor returned <0x%X>\n", dwRC);

	if (dwRC == USBIO_ERR_SUCCESS)
		{
		USB_COMMON_DESCRIPTOR* Desc;
		ULONG rl = dwByteCount;
		ULONG ulDescLength = 0;
		CHAR* data = szBuffer;

		while (rl > 0)
			{
			Desc = (USB_COMMON_DESCRIPTOR*) data;
			ulDescLength = Desc->bLength;
			if ((ulDescLength > rl) || (ulDescLength == 0))
				{
				printf("Length remaining too short!\n");
				rl = 0;
				}
			else
				{
				switch (Desc->bDescriptorType)
					{
				case USB_CONFIGURATION_DESCRIPTOR_TYPE:
					pConfigDescriptor =
						(USB_CONFIGURATION_DESCRIPTOR*) data;
					if (VerboseMode)
						{
						printf("\nCONFIGURATION DESCRIPTOR:\n"
							   "bLength = <%u>\n"
							   "bDescriptorType = <%u>\n"
							   "wTotalLength = <%u>\n"
							   "bNumInterfaces = <%u>\n"
							   "bConfigurationValue = <%u>\n"
							   "iConfiguration = <%u>\n"
							   "bmAttributes = <%u>\n"
							   "MaxPower = <%u>\n",
							   pConfigDescriptor->bLength,
							   pConfigDescriptor->bDescriptorType,
							   pConfigDescriptor->wTotalLength,
							   pConfigDescriptor->bNumInterfaces,
							   pConfigDescriptor->bConfigurationValue,
							   pConfigDescriptor->iConfiguration,
							   pConfigDescriptor->bmAttributes,
							   pConfigDescriptor->MaxPower);
						}
					break;
				case USB_INTERFACE_DESCRIPTOR_TYPE:
					pInterfaceDescriptor =
						(USB_INTERFACE_DESCRIPTOR*) data;
					if (VerboseMode)
						{
						printf("\nINTERFACE DESCRIPTOR: \n"
							   "bLength = <%u>\n"
							   "bDescriptorType = <%u>\n"
							   "bInterfaceNumber = <%u>\n"
							   "bAlternateSetting = <%u>\n"
							   "bNumEndpoints = <%u>\n"
							   "bInterfaceClass = <%u>\n"
							   "bInterfaceSubClass = <%u>\n"
							   "bInterfaceProtocol = <%u>\n"
							   "iInterface = <%u>\n",
							   pInterfaceDescriptor->bLength,
							   pInterfaceDescriptor->bDescriptorType,
							   pInterfaceDescriptor->bInterfaceNumber,
							   pInterfaceDescriptor->bAlternateSetting,
							   pInterfaceDescriptor->bNumEndpoints,
							   pInterfaceDescriptor->bInterfaceClass,
							   pInterfaceDescriptor->bInterfaceSubClass,
							   pInterfaceDescriptor->bInterfaceProtocol,
							   pInterfaceDescriptor->iInterface);
						}
					break;
				case USB_ENDPOINT_DESCRIPTOR_TYPE:
					pEndpointDescriptor =
						(USB_ENDPOINT_DESCRIPTOR*) data;
					if (VerboseMode)
						{
						printf("\nENDPOINT DESCRIPTOR: \n"
							   "bLength = <%u>\n"
							   "bDescriptorType = <%u>\n"
							   "bEndpointAddress = <%u>\n"
							   "bmAttributes = <%u>\n"
							   "wMaxPacketSize = <%u>\n"
							   "bInterval = <%u>\n",
							   pEndpointDescriptor->bLength,
							   pEndpointDescriptor->bDescriptorType,
							   pEndpointDescriptor->bEndpointAddress,
							   pEndpointDescriptor->bmAttributes,
							   pEndpointDescriptor->wMaxPacketSize,
							   pEndpointDescriptor->bInterval);
						}
					break;
				default:
					break;
					}
				}
			data += ulDescLength;
			rl -= ulDescLength;
			}
		}
	PRINT_IF_VERBOSE("\n");
	}


static void GetStringDescriptor()
	{
	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 = g_UsbDev.GetStringDescriptor((USB_STRING_DESCRIPTOR*) szBuffer,
										dwByteCount, 1, 0);
	PRINT_IF_VERBOSE1("CUsbIo::GetStringDescriptor returned <0x%X>\n", dwRC);

	if (dwRC == USBIO_ERR_SUCCESS)
		{
		pStringDescriptor = (USB_STRING_DESCRIPTOR*) szBuffer;
		if (VerboseMode)
			{
			printf("\nSTRING DESCRIPTOR:\n"
				   "bLength = <%u>\n"
				   "bDescriptorType = <%u>\n"
				   "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_VERBOSE1("%c", *Ptr);
			}
		PRINT_IF_VERBOSE(">\n\n");
		}
	}


static void SetConfiguration()
	{
	USBIO_SET_CONFIGURATION SetConfig;

	memset(&SetConfig, 0, sizeof(USBIO_SET_CONFIGURATION));

	// Set the first configuration as active
	SetConfig.ConfigurationIndex = 0;
	SetConfig.NbOfInterfaces = 1;
	SetConfig.InterfaceList[0].InterfaceIndex = 0;
	SetConfig.InterfaceList[0].AlternateSettingIndex = 0;
	SetConfig.InterfaceList[0].MaximumTransferSize = KBufferSize;
	dwRC = g_UsbDev.SetConfiguration(&SetConfig);
	PRINT_IF_VERBOSE1("CUsbIo::SetConfiguration returned <0x%X>\n", dwRC);
	}


static void GetConfigurationInfo()
	{
	USBIO_CONFIGURATION_INFO ConfigInfo;
	USHORT i = 0;

	memset(&ConfigInfo, 0, sizeof(USBIO_CONFIGURATION_INFO));

	dwRC = g_UsbDev.GetConfigurationInfo(&ConfigInfo);
	PRINT_IF_VERBOSE1("CUsbIo::GetConfigurationInfo returned <0x%X>\n", dwRC);

	if (dwRC == USBIO_ERR_SUCCESS)
		{
		if (VerboseMode)
			{
			printf("\nCONFIGURATION INFO:\n"
				   "NbOfInterfaces = <%lu>\n"
				   "NbOfPipes = <%lu>\n",
				   ConfigInfo.NbOfInterfaces,
				   ConfigInfo.NbOfPipes);
			}
		for (i = 0; i < ConfigInfo.NbOfInterfaces; i++)
			{
			if (VerboseMode)
				{
				printf("\nINTERFACE <%u>:\n", i + 1);
				printf("InterfaceNumber = <%u>\n"
					   "AlternateSetting = <%u>\n"
					   "Class = <%u>\n"
					   "SubClass = <%u>\n"
					   "Protocol = <%u>\n"
					   "NumberOfPipes = <%u>\n"
					   "reserved1 = <%u>\n"
					   "reserved2 = <%u>\n",
					   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("\n");
			if ((ConfigInfo.PipeInfo[i].PipeType == PipeTypeBulk) &&
				!(ConfigInfo.PipeInfo[i].EndpointAddress & 0x80))
				{
				PRINT_IF_VERBOSE("Bulk OUT pipe found:\n");
				g_ucBulkOutEndpoint = ConfigInfo.PipeInfo[i].EndpointAddress;
				maxOutPacketSize = ConfigInfo.PipeInfo[i].MaximumPacketSize;
				}
			else if ((ConfigInfo.PipeInfo[i].PipeType == PipeTypeBulk) &&
					 (ConfigInfo.PipeInfo[i].EndpointAddress & 0x80))
				{
				PRINT_IF_VERBOSE("Bulk IN pipe found:\n");
				g_ucBulkInEndpoint = ConfigInfo.PipeInfo[i].EndpointAddress;
				}
			if (VerboseMode)
				{
				printf("PIPE <%u>\n", i + 1);
				printf("PipeType = <%d>\n"
					   "MaximumTransferSize = <%lu>\n"
					   "MaximumPacketSize = <%u>\n"
					   "EndpointAddress = <%u>\n"
					   "Interval = <%u>\n"
					   "InterfaceNumber = <%u>\n"
					   "reserved1 = <%u>\n"
					   "reserved2 = <%u>\n"
					   "reserved3 = <%u>\n",
					   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("\n");
	}


static void OpenPipes()
	{
	CUsbIo::DestroyDeviceList(g_DevList);

	// Enumerate attached USB devices supported by USBIO
	g_DevList = CUsbIo::CreateDeviceList(&g_UsbioID);

	// Create the bulk OUT pipe
	dwRC = g_BulkOutPipe.Bind(0, g_ucBulkOutEndpoint, g_DevList, &g_UsbioID);
	PRINT_IF_VERBOSE1("CUsbIoPipe::Bind (Bulk OUT) returned <0x%X>\n", dwRC);

	if (dwRC == USBIO_ERR_SUCCESS)
		{
		// Create the bulk IN pipe
		dwRC = g_BulkInPipe.Bind(0, g_ucBulkInEndpoint, g_DevList, &g_UsbioID);
		PRINT_IF_VERBOSE1("CUsbIoPipe::Bind (Bulk IN) returned <0x%X>\n", dwRC);
		}
	PRINT_IF_VERBOSE("\n");
	}


static void ClosePipes()
	{
	// Close down the bulk OUT pipe
	dwRC = g_BulkOutPipe.Unbind();
	PRINT_IF_VERBOSE1("CUsbIoPipe::Unbind (Bulk OUT) returned <0x%X>\n", dwRC);

	if (dwRC == USBIO_ERR_SUCCESS)
		{
		// Close down the bulk IN pipe
		dwRC = g_BulkInPipe.Unbind();
		PRINT_IF_VERBOSE1("CUsbIoPipe::Unbind (Bulk IN) returned <0x%X>\n", dwRC);
		}
	}


static void ReceiveVersion()
	{
	// Here we (hope to) read an 8 byte packet containing the T_USB version.
	printf("* Waiting for T_USB version packet to arrive...");

	// The first 4 bytes are interpreted as an int32 value.
	DWORD bytes_read = 0;
	g_Buf.NumberOfBytesToTransfer = KPreambleLength;
	g_BulkInPipe.Read(&g_Buf);
	dwRC = g_BulkInPipe.WaitForCompletion(&g_Buf, INFINITE);
	printf(" done.\n");
	bytes_read = g_Buf.BytesTransferred;
	if (dwRC == USBIO_ERR_SUCCESS)
		{
		if (bytes_read < KPreambleLength)
			{
			printf("* Read less bytes (%d) than expected (%d).\n",
				   bytes_read, KPreambleLength);
			dwRC = USBIO_ERR_FAILED;
			return;
			}
		}
	else
		{
		printf("\n* Error: CUsbIoPipe::Read (version) returned <0x%X>\n", dwRC);
		return;
		}
	// First make sure it's actually the version packet, and not
	// a data preamble packet of an old T_USB.
	if (!(Data[4] == 'V' &&
		  Data[5] == 'e' &&
		  Data[6] == 'r' &&
		  Data[7] == 's'))
		{
		printf("* Inadequate version of T_USB: no version packet was sent (we need at least %d)\n",
			   KTusbVersion);
		dwRC = USBIO_ERR_FAILED;
		return;
		}
	DWORD tusb_version = *((ULONG*) Data);						// first 4 bytes
	if (tusb_version < KTusbVersion)
		{
		printf("* Inadequate version of T_USB: %d (we need at least %d)\n",
			   tusb_version, KTusbVersion);
		dwRC = USBIO_ERR_FAILED;
		return;
		}
	printf("* Suitable version of T_USB found: %d.\n", tusb_version);
	}


static void SendVersion()
	{
	// Here we send an 8 byte packet containing USBRFLCT's + USBIO's versions.
	printf("* Sending our version packet to T_USB...");

	DWORD bytes_written = 0;
	g_Buf.NumberOfBytesToTransfer = KPreambleLength;
	Data[0] = VERSION_MAJOR;
	Data[1] = VERSION_MINOR;
	Data[2] = VERSION_MICRO;
	Data[3] = USBIO_VERSION_MAJOR;
	Data[4] = USBIO_VERSION_MINOR;
	g_BulkOutPipe.Write(&g_Buf);
	dwRC = g_BulkOutPipe.WaitForCompletion(&g_Buf, INFINITE);
	printf(" done.\n");
	bytes_written = g_Buf.BytesTransferred;
	if (dwRC == USBIO_ERR_SUCCESS)
		{
		if (bytes_written < KPreambleLength)
			{
			printf("* Wrote less bytes (%d) than requested (%d).\n",
				   bytes_written, KPreambleLength);
			dwRC = USBIO_ERR_FAILED;
			}
		}
	else
		{
		printf("\n* Error: CUsbIoPipe::Write (version) returned <0x%X>\n", dwRC);
		}
	}


static void ExchangeVersions()
	{
	SendVersion();
	if (dwRC != USBIO_ERR_SUCCESS)
		return;
	ReceiveVersion();
	}


static void GetLength()
	{
	// The first two bytes are interpreted as a length value.
	DWORD bytes_read = KPreambleLength;
	g_Buf.NumberOfBytesToTransfer = KPreambleLength;

	g_BulkInPipe.Read(&g_Buf);
	dwRC = g_BulkInPipe.WaitForCompletion(&g_Buf, INFINITE);
	bytes_read = g_Buf.BytesTransferred;

	if (dwRC == USBIO_ERR_SUCCESS)
		{
		if (bytes_read < KPreambleLength)
			{
			printf("* Read less bytes (%d) than expected (%d).\n",
				   bytes_read, KPreambleLength);
			dwRC = USBIO_ERR_FAILED;
			return;
			}
		}
	else
		{
		printf("\n* Error: CUsbIoPipe::Read (length) returned <0x%X>\n", dwRC);
		return;
		}
	Length = *((ULONG*) Data);								// first 4 bytes
	if (Length > KBufferSize)
		{
		printf("* This is too much: %d (our buffer is too small: %d)\n",
			   Length, KBufferSize);
		dwRC = USBIO_ERR_FAILED;
		}
	if (VerboseMode)
		{
		printf("* Just read %d bytes, now assuming transfer length is %d bytes.\n",
			   bytes_read, Length);
		}
	else if (TransferMode == EReceiveOnly || TransferMode == ETransmitOnly)
		{
		printf("* Single transfer size: %d bytes.\n", Length);
		}
	}


static void ReadData()
	{
	// 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:
	if (Length == 0)
		g_Buf.NumberOfBytesToTransfer = 1;
	else
		g_Buf.NumberOfBytesToTransfer = Length;

	g_BulkInPipe.Read(&g_Buf);
	dwRC = g_BulkInPipe.WaitForCompletion(&g_Buf, INFINITE);
	DWORD bytes_read = g_Buf.BytesTransferred;

	if (dwRC != USBIO_ERR_SUCCESS)
		{
		printf("\n* Error: CUsbIoPipe::Read (data) returned <0x%X>\n", dwRC);
		}
	else
		{
		if (bytes_read != Length)
			{
			printf("* Read more/less bytes (%d) than expected (%d).\n",
				   bytes_read, Length);
			dwRC = USBIO_ERR_FAILED;
			}
		else
			{
			PRINT_IF_VERBOSE1("* Read %d bytes.\n", Length);
			}
		}
	}


static void WriteData()
	{
	DWORD bytes_written = Length;

	g_Buf.NumberOfBytesToTransfer = bytes_written;

	g_BulkOutPipe.Write(&g_Buf);
	dwRC = g_BulkOutPipe.WaitForCompletion(&g_Buf, INFINITE);
	if (ZlpMode && (Length >= maxOutPacketSize) && ((Length % maxOutPacketSize) == 0))
	{
		// writes a zero length packet
		g_BulkOutPipe.Write(&g_ZlpBuf);
		dwRC = g_BulkOutPipe.WaitForCompletion(&g_ZlpBuf, INFINITE);
	}


	bytes_written = g_Buf.BytesTransferred;

	if (dwRC == USBIO_ERR_SUCCESS)
		{
		PRINT_IF_VERBOSE1("* Wrote %d bytes.\n", bytes_written);
		}
	else
		{
		printf("\n* Error: CUsbIoPipe::Write returned <0x%X>\n", dwRC);
		}
	}


void PrintStats()
	{
	static DWORD loop = 0;									// the loop counter
	static double xfer_size = 0;							// the total transfer amount so far
	time_t t_1 = time(NULL);								// current time
	double t_diff = difftime(t_1, T_0);						// this yields current seconds since start
	xfer_size += (KPreambleLength + (2 * Length)) * KLoopModeDisplayUpdate;	//
	double xfer_rate = xfer_size / t_diff;					// mean transfer rate since start
	loop += KLoopModeDisplayUpdate;
	printf("* Iter: %d (%d bytes) Total: %.0f bytes Rate: %.0f bytes/s  \r",
		   loop, Length, xfer_size, xfer_rate);
	}


void PrintStatsEveryLoop()
	{
	static DWORD loop = 0;									// the loop counter
	static double xfer_size = 0;							// the total transfer amount so far
	time_t t_1 = time(NULL);								// current time
	double t_diff = difftime(t_1, T_0);						// this yields current seconds since start
	xfer_size += (KPreambleLength + (2 * Length));			//
	double xfer_rate = xfer_size / t_diff;					// mean transfer rate since start
	printf("* Iter: %d (%d bytes) Total: %.0f bytes Rate: %.0f bytes/s  \r",
		   ++loop, Length, xfer_size, xfer_rate);
	}


void PrintUnidirStats()
	{
	static DWORD loop = 0;									// the loop counter
	static double xfer_size = 0;							// the total transfer amount so far
	time_t t_1 = time(NULL);								// current time
	double t_diff = difftime(t_1, T_0);						// this yields current seconds since start
	xfer_size += Length * KUniModeDisplayUpdate;
	double xfer_rate = xfer_size / t_diff;					// mean transfer rate since start
	loop += KUniModeDisplayUpdate;
	printf("* Iter: %d (%d bytes) Total: %.0f bytes Rate: %.0f bytes/s  \r",
		   loop, Length, xfer_size, xfer_rate);
	}


static void LoopTransfer()
	{
	printf("* Loop Transfers -- reading & writing alternately.\n");

	T_0 = time(NULL);										// starting time

	while (dwRC == USBIO_ERR_SUCCESS)
		{
		static DWORD n = 0;

		// First we get the length (+ the packet size)
		GetLength();

		if (dwRC == USBIO_ERR_SUCCESS)
			{
			// Then we read 'Length' bytes
			ReadData();
			}

		if (dwRC == USBIO_ERR_SUCCESS)
			{
			// Now we send the received data back to the client.
			WriteData();
			}

		if (dwRC == USBIO_ERR_SUCCESS)
			{
			// Finally, sometimes we print some statistics
			if (TransferMode == ELoopDebug)
				PrintStatsEveryLoop();
			else if ((++n % KLoopModeDisplayUpdate) == 0)
				PrintStats();
			}
		}
	}


static void ReceiveOnlyTransfer()
	{
	printf("* Receive-only transfers (IN).\n");

	// First (and only once) we get the transfer length (+ the packet size)
	GetLength();

	T_0 = time(NULL);										// starting time

	while (dwRC == USBIO_ERR_SUCCESS)
		{
		static DWORD n = -1;
		static DWORD pktNum;

		// Then we read 'Length' bytes
		ReadData();
		pktNum = *(DWORD *)&Data;
		if (pktNum != ++n)
			{
			printf ("\n* Error: rcv'd wrong pkt number: 0x%x (expected: 0x%x)\n", pktNum, n);
			// reset from the received packet number, so that ...
			// if a packet is lost or duplicated a single error is reported
			n = pktNum;
			}
		// Finally, sometimes we print some statistics
		if ((n % KUniModeDisplayUpdate) == 0)
			PrintUnidirStats();
		}
	}


static void TransmitOnlyTransfer()
	{
	printf("* Transmit-only transfers (OUT).\n");

	// First (and only once) we get the transfer length (+ the packet size)
	GetLength();

	T_0 = time(NULL);										// starting time

	while (dwRC == USBIO_ERR_SUCCESS)
		{
		static DWORD n = 0;
		// First the packet number is put into the first four bytes
		*(DWORD *)&Data = n++;
		// Then we write 'Length' bytes
		WriteData();

		// Finally, sometimes we print some statistics
		if ((n % KUniModeDisplayUpdate) == 0)
			PrintUnidirStats();
		}
	}


static void DoTransfers()
	{
	switch (TransferMode)
		{
	case ELoop:
	case ELoopDebug:
		LoopTransfer();
		break;
	case EReceiveOnly:
		ReceiveOnlyTransfer();
		break;
	case ETransmitOnly:
		TransmitOnlyTransfer();
		break;
	default:
		dwRC = -1;
		break;
		}
	}


static void Delay(int milliseconds)
	{
	printf("* Short wait... ");
	Sleep(milliseconds);
	printf("done.\n");
	}


static void PrintHello()
	{
	printf("*--------------------------------------------------\n");
	printf("* USBRFLCT v%d.%d.%d (for use with USBRFLCT.SYS v%d.%d)\n",
		   VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO,
		   USBIO_VERSION_MAJOR, USBIO_VERSION_MINOR);
	printf("* USB Reflector Test Program / T_USB Host-side Part\n");
	printf("*   Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).\n");
	printf("*--------------------------------------------------\n");
	}


int main(int argc, char* argv[])
	{
	PrintHello();

	if (ProcessCmdLine(argc, argv) != 0)
		return -1;

	OpenUsbDevice();

	if (dwRC == USBIO_ERR_SUCCESS)
		{
		GetDeviceDescriptor();
		}
	if (dwRC == USBIO_ERR_SUCCESS)
		{
		GetConfigurationDescriptor();
		}
	if (dwRC == USBIO_ERR_SUCCESS)
		{
		GetStringDescriptor();
		}
	if (dwRC == USBIO_ERR_SUCCESS)
		{
		SetConfiguration();
		}
	if (dwRC == USBIO_ERR_SUCCESS)
		{
		// In order to give the USB device-side program (t_usb)
		// enough time after getting configured to carry out
		// some device tests, we wait here for a short while
		// before proceeding:
		Delay(2000);
		GetConfigurationInfo();
		}
	if (dwRC == USBIO_ERR_SUCCESS)
		{
		OpenPipes();
		}
	if (dwRC == USBIO_ERR_SUCCESS)
		{
		ExchangeVersions();
		}
	if (dwRC == USBIO_ERR_SUCCESS)
		{
		DoTransfers();
		}
	if (dwRC == USBIO_ERR_SUCCESS)
		{
		ClosePipes();
		}

	CloseUsbDevice();

	return 0;
	}


// --eof