usbmgmt/usbmgrtest/t_usbman/src/t_usbman.cpp
changeset 0 c9bc50fca66e
child 25 4ddb65515edd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbmgmt/usbmgrtest/t_usbman/src/t_usbman.cpp	Tue Feb 02 02:02:59 2010 +0200
@@ -0,0 +1,1164 @@
+/*
+* Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "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:
+* This test program provides menu-driven functional tests for the USB Manager which exercise
+* every exported API it provides. Note that, to use this program with Hurricane USB, the macro
+* HURRICANE should be defined in the t_usbman.mmp file.
+* WARNING: Some of these tests rely on the 'dummy class controller' build of 
+* USBMAN. This is produced with t_usbman_dummycc.mmp, and it includes three 
+* instances of the dummy class controller (mainly used for GT171 automated 
+* tests). To get this to operate correctly a suitable ini file must be copied 
+* to c:\dummy.ini, for instance usbman\data\test.ini, which provides delayed 
+* asynchronous Start and Stop behaviour for each of the dummy CC instances.
+*
+*/
+
+#include <e32base.h>
+#include <e32math.h>
+#include <e32cons.h>
+#include <badesca.h>
+#include <c32comm.h>
+#include <usbman.h>
+#include <e32property.h> //Publish & Subscribe header, required for setting serial number
+#include "usb.h"
+
+LOCAL_D CConsoleBase* console;
+
+RTimer TheTimer;
+RCommServ TheCommServ;
+RUsb TheUsb;
+RUsb TheSecondUsbSession;
+TBool ThePrimaryClient;
+
+//_LIT(KUsbCsyName, "ECACM");
+//_LIT(KUsbPortName, "ACM::0");
+_LIT(KUsbLddName, "EUSBC");
+
+// These are used in the startup stress test.
+TInt64 TheRandomSeed = 0;
+const TInt KMaxCancelTime = 2000000;
+const TInt KMaxStopTime = 10000000;
+
+#define _printf console->Printf
+#define _getch console->Getch
+#define LEAVE(_x) VerboseLeaveL(_x, __LINE__, TPtrC8((const TUint8*)__FILE__), _L8(#_x))
+#define LEAVEIFERROR(_x) VerboseLeaveIfErrorL(_x, __LINE__, TPtrC8((const TUint8*)__FILE__), _L8(#_x))
+#define CHECK(_x) if (! (_x)) VerboseLeaveL(KErrGeneral, __LINE__, TPtrC8((const TUint8*)__FILE__), _L8(#_x))
+
+////////////////////////////////////////////////////////////////////////////////
+// Helper functions below this point
+////////////////////////////////////////////////////////////////////////////////
+
+void VerboseLeaveL(TInt aError, TInt aLineNum, const TDesC8& aFileName, const TDesC8& aCode)
+	{
+	TInt filenameOffset = aFileName.LocateReverse('\\') + 1;
+	if (filenameOffset < 0) filenameOffset = 1;
+	TPtrC8 shortFileName = aFileName.Mid(filenameOffset);
+	TBuf<64> fName, code;
+	fName.Copy(shortFileName.Left(64));
+	code.Copy(aCode.Left(64));
+	_printf(_L("ERROR (%d) on line %d of file %S\n"), aError, aLineNum, &fName);
+	_printf(_L("Code: %S\n\n"), &code);
+	_printf(_L("[ press any key ]"));
+	_getch();
+	User::Leave(aError);
+	}
+
+void VerboseLeaveIfErrorL(TInt aError, TInt aLineNum, const TDesC8& aFileName, const TDesC8& aCode)
+	{
+	if (aError) 
+		VerboseLeaveL(aError, aLineNum, aFileName, aCode);
+	}
+
+void ReadString(TDes& aDes)
+/**
+ * Reads user input into the start of the descriptor aDes.
+ */
+	{
+	TChar inputKey;
+	TInt count = 0;
+
+	aDes.Zero();
+	for (;;)
+		{
+		inputKey = (TInt) _getch();
+
+		if ((TInt)inputKey == EKeyEnter)
+			break;
+
+		if(inputKey == EKeyBackspace)
+			{
+			if (count > 0)
+				{
+ 				_printf(_L("%C"), (TUint) inputKey);
+				aDes.Delete(--count,1);
+				}
+			}
+		else if(inputKey.IsPrint())
+			{
+			_printf(_L("%C"), (TUint) inputKey);
+			aDes.Append(inputKey);
+			count++;
+			}
+		}
+	}
+
+
+void PrintUsbServiceState(TUsbServiceState aServiceState)
+	{
+	switch (aServiceState)
+		{
+	case EUsbServiceIdle: _printf(_L("USB service is idle.\n")); break;
+	case EUsbServiceStarting: _printf(_L("USB service is starting.\n")); break;
+	case EUsbServiceStarted: _printf(_L("USB service is started.\n")); break;
+	case EUsbServiceStopping: _printf(_L("USB service is stopping.\n")); break;
+#ifndef HURRICANE
+	case EUsbServiceFatalError: _printf(_L("USB service has a fatal error.\n")); break;
+#endif // HURRICANE
+	default:
+		_printf(_L("ERROR: unknown service state!\n"));
+		break;
+		}
+	}
+
+void PrintUsbDeviceState(TUsbDeviceState aDeviceState)
+	{
+	switch (aDeviceState)
+		{
+	case EUsbDeviceStateUndefined:
+		_printf(_L("USB device state is undefined.\n"));
+		break;
+	case EUsbDeviceStateDefault:
+		_printf(_L("USB device state is default.\n"));
+		break;
+	case EUsbDeviceStateAttached:
+		_printf(_L("USB device state is attached.\n"));
+		break;
+	case EUsbDeviceStatePowered:
+		_printf(_L("USB device state is powered.\n"));
+		break;
+	case EUsbDeviceStateConfigured:
+		_printf(_L("USB device state is configured.\n"));
+		break;
+	case EUsbDeviceStateAddress:
+		_printf(_L("USB device state is address.\n"));
+		break;
+	case EUsbDeviceStateSuspended:
+		_printf(_L("USB device state is suspended.\n"));
+		break;
+	default:
+		_printf(_L("ERROR: unknown device state!\n"));
+		break;
+		}
+	}
+
+////////////////////////////////////////////////////////////////////////////////
+// Test cases below this point
+////////////////////////////////////////////////////////////////////////////////
+
+void OpenUsbL()
+	{
+	LEAVEIFERROR(TheUsb.Connect());
+	}
+
+void CloseUsbL()
+	{
+	TheUsb.Close();
+	}
+
+// USB test cases 3.1.1, 3.1.2 and 3.1.4, startup from primary and non-primary sessions.
+void StartUsbL()
+	{
+	TRequestStatus status;
+	TheUsb.Start(status);
+	User::WaitForRequest(status);
+	LEAVEIFERROR(status.Int());
+
+	_printf(_L("Started USB.\n"));
+	}
+
+// USB test cases 3.2 and (part of) 3.4, startup cancellation.
+void CancelStartingUsbL(TBool aCancelByStopping)
+	{
+	TRequestStatus cancelStatus;
+	TRequestStatus timerStatus;
+	TInt cancelTime = 0;
+
+	while (cancelTime < (100 * 1000000))
+		{
+		_printf(_L("Cancelling after %d microseconds\n"), cancelTime);
+
+		TheTimer.After(timerStatus, cancelTime);
+		TheUsb.Start(cancelStatus);
+
+		User::WaitForRequest(cancelStatus, timerStatus);
+
+		// If the request has finished before the timer completed,
+		// cancel the timer and check the request status for errors.
+		if (timerStatus == KRequestPending)
+			{
+			TheTimer.Cancel();
+			User::WaitForRequest(timerStatus);
+			LEAVEIFERROR(cancelStatus.Int());
+			break;
+			}
+
+		// The timer should never complete with an error.
+		LEAVEIFERROR(timerStatus.Int());
+		
+		// Cancel the start. The option of cancelling by stopping is only available
+		// from version 7.0s, because in Hurricane a stop while starting is
+		// actually implemented as a cancel.
+
+#ifndef HURRICANE
+		if (aCancelByStopping)
+			{
+			TRequestStatus stopStatus;
+			TheUsb.Stop(stopStatus);
+			User::WaitForRequest(stopStatus);
+			LEAVEIFERROR(stopStatus.Int());
+			}
+		else
+#endif // HURRICANE
+			{
+			TheUsb.StartCancel();
+			}
+
+		User::WaitForRequest(cancelStatus);
+
+		// If the start actually completed, just return.
+		if (cancelStatus == KErrNone)
+			break;
+
+#ifndef HURRICANE
+		// If we're cancelling by stopping, then getting this error code is
+		// expected.
+		if (cancelStatus == KErrUsbServiceStopped)
+			{
+			cancelTime += 50000;
+			continue;
+			}
+#endif // HURRICANE
+
+		if (cancelStatus != KErrCancel)
+			LEAVE(cancelStatus.Int());
+
+		cancelTime += 50000;
+		}
+
+	TUsbServiceState serviceState;
+	LEAVEIFERROR(TheUsb.GetCurrentState(serviceState));
+	PrintUsbServiceState(serviceState);
+	}
+
+// Part of USB test case 3.4, stopping class controllers.
+void StopUsbSynchronousL()
+	{
+	TheUsb.Stop();
+	_printf(_L("Stopped USB.\n"));
+	}
+
+#ifndef HURRICANE
+
+// Part of USB test case 3.4, stopping class controllers.
+void StopUsbAsynchronousL()
+	{
+	TRequestStatus status;
+
+	TheUsb.Stop(status);
+	User::WaitForRequest(status);
+	LEAVEIFERROR(status.Int());
+
+	_printf(_L("Stopped USB.\n"));
+	}
+
+void CancelStoppingUsbL(TBool aCancelByStarting)
+	{
+	TRequestStatus cancelStatus;
+	TRequestStatus timerStatus;
+	TInt cancelTime = 0;
+
+	while (cancelTime < (100 * 1000000))
+		{
+		_printf(_L("Cancelling after %d microseconds\n"), cancelTime);
+
+		TheTimer.After(timerStatus, cancelTime);
+		TheUsb.Stop(cancelStatus);
+
+		User::WaitForRequest(cancelStatus, timerStatus);
+
+		// If the request has finished before the timer completed,
+		// cancel the timer and check the request status for errors.
+		if (timerStatus == KRequestPending)
+			{
+			TheTimer.Cancel();
+			User::WaitForRequest(timerStatus);
+			LEAVEIFERROR(cancelStatus.Int());
+			break;
+			}
+
+		// The timer should never complete with an error.
+		LEAVEIFERROR(timerStatus.Int());
+
+#ifndef HURRICANE 
+		if (aCancelByStarting) 
+			{ 
+			TRequestStatus startStatus; 
+			TheUsb.Start(startStatus); 
+			User::WaitForRequest(startStatus); 
+			LEAVEIFERROR(startStatus.Int()); 
+			} 
+		else 
+#endif // HURRICANE 
+			{ 
+			TheUsb.StopCancel(); 
+			}
+
+		User::WaitForRequest(cancelStatus);
+
+		// If the stop actually completed, just return.
+		if (cancelStatus == KErrNone)
+			break;
+
+#ifndef HURRICANE 
+		// If we're cancelling by starting, then getting this 
+		// error code is expected. 
+	    if (cancelStatus == KErrUsbServiceStarted) 
+			{ 
+			cancelTime += 50000; 
+			continue; 
+			} 
+#endif // HURRICANE 
+	 
+		if (cancelStatus != KErrCancel)
+			LEAVE(cancelStatus.Int());
+
+		cancelTime += 50000;
+		}
+
+	TUsbServiceState serviceState;
+	LEAVEIFERROR(TheUsb.GetCurrentState(serviceState));
+	PrintUsbServiceState(serviceState);
+	}
+
+#endif // HURRICANE
+
+void RestartUsbL()
+	{
+	TheUsb.Stop();
+
+	TRequestStatus status;
+	TheUsb.Start(status);
+	User::WaitForRequest(status);
+	LEAVEIFERROR(status.Int());
+
+	_printf(_L("\nRestarted USB.\n"));
+	}
+
+// USB test case 3.6, stress testing of startup and shutdown.
+void StartupStressTestL()
+	{
+	TRequestStatus cancelStatus;
+	TRequestStatus timerStatus;
+	TInt cancelTime = Math::Rand(TheRandomSeed) % KMaxCancelTime;
+	TInt stopTime;
+
+	for (TInt i = 0; i < 100; i++)
+		{
+		_printf(_L("Cancelling after %d microseconds\n"), cancelTime);
+
+		TheTimer.After(timerStatus, cancelTime);
+		TheUsb.Start(cancelStatus);
+
+		User::WaitForRequest(cancelStatus, timerStatus);
+
+		// If the request has finished before the timer completed,
+		// cancel the timer and check the request status for errors.
+		if (timerStatus == KRequestPending)
+			{
+			TheTimer.Cancel();
+			LEAVEIFERROR(cancelStatus.Int());
+			}
+		else
+			{
+			// The timer should never complete with an error.
+			LEAVEIFERROR(timerStatus.Int());
+
+			// Cancel the start.
+			TheUsb.StartCancel();
+			User::WaitForRequest(cancelStatus);
+			}
+
+		// If the start actually completed, then try stopping.
+		if (cancelStatus == KErrNone)
+			{
+			stopTime = Math::Rand(TheRandomSeed) % KMaxStopTime;
+			_printf(_L("Stopping after %d microseconds\n"), stopTime);
+			User::After(stopTime);
+			TheUsb.Stop();
+			}
+		else if (cancelStatus != KErrCancel)
+			{
+			LEAVE(cancelStatus.Int());
+			}
+
+		cancelTime = Math::Rand(TheRandomSeed) % KMaxCancelTime;
+		}
+
+	_printf(_L("USB is now stopped\n"));
+	}
+
+// USB test case 3.3, monitoring the state of the USB.
+void MonitorUsbDeviceStateL()
+	{
+	TRequestStatus consoleStatus;
+	TRequestStatus notifyStatus;
+	TUsbServiceState serviceState;
+	TUsbDeviceState deviceState;
+
+	LEAVEIFERROR(TheUsb.GetCurrentState(serviceState));
+
+	_printf(_L("\n"));
+
+	PrintUsbServiceState(serviceState);
+
+#ifndef HURRICANE
+	LEAVEIFERROR(TheUsb.GetDeviceState(deviceState));
+	PrintUsbDeviceState(deviceState);
+#endif // HURRICANE
+
+	_printf(_L("\nMonitoring the state of USB - press any key to stop\n\n"));
+
+	console->Read(consoleStatus);
+
+	while (consoleStatus == KRequestPending)
+		{
+		TheUsb.StateNotification(0xFFFFFFFF, deviceState, notifyStatus);
+		User::WaitForRequest(notifyStatus, consoleStatus);
+
+		if (consoleStatus == KRequestPending)
+			{
+			if (notifyStatus != KErrNone)
+				{
+				// need to cancel the read to avoid a CBase-77 panic!
+				console->ReadCancel();
+				LEAVE(notifyStatus.Int());
+				}
+			}
+		else
+			{
+			// The user's pressed a key.
+			TheUsb.StateNotificationCancel();
+			User::WaitForRequest(notifyStatus);
+			break;
+			}
+
+		PrintUsbDeviceState(deviceState);
+		}
+
+	_printf(_L("\nTest complete\n"));
+	}
+
+// Concurrency test case (3.9.1 in test spec). One client starts USB and then another
+// one stops it.
+void StartAThenStopBL()
+	{
+	LEAVEIFERROR(TheSecondUsbSession.Connect());
+
+	TRequestStatus status;
+	TUsbServiceState serviceState;
+
+	// Start on client 1.
+	TheUsb.Start(status);
+	User::WaitForRequest(status);
+	LEAVEIFERROR(status.Int());
+
+	// Get status on both sessions.
+
+	LEAVEIFERROR(TheUsb.GetCurrentState(serviceState));
+	_printf(_L("\nSession 1: "));
+	PrintUsbServiceState(serviceState);
+
+	LEAVEIFERROR(TheSecondUsbSession.GetCurrentState(serviceState));
+	_printf(_L("Session 2: "));
+	PrintUsbServiceState(serviceState);
+
+	// Stop on client 2.
+	TheSecondUsbSession.Stop();
+
+	// Get status on both sessions.
+
+	LEAVEIFERROR(TheUsb.GetCurrentState(serviceState));
+	_printf(_L("Session 1: "));
+	PrintUsbServiceState(serviceState);
+
+	LEAVEIFERROR(TheSecondUsbSession.GetCurrentState(serviceState));
+	_printf(_L("Session 2: "));
+	PrintUsbServiceState(serviceState);
+
+	TheSecondUsbSession.Close();
+	}
+
+// Concurrency test case (3.9.2 in test spec). One client stops USB while it's being
+// started by another one.
+void StopBWhileAStartingL()
+	{
+	LEAVEIFERROR(TheSecondUsbSession.Connect());
+
+	TRequestStatus status;
+	TUsbServiceState serviceState;
+
+	// Start on client 1.
+	TheUsb.Start(status);
+
+	// Stop on client 2.
+	TheSecondUsbSession.Stop();
+
+	// Wait for start request to complete.
+	User::WaitForRequest(status);
+	if (status.Int() != KErrUsbServiceStopped)
+		LEAVEIFERROR(status.Int());
+
+	// Get status on both sessions.
+
+	LEAVEIFERROR(TheUsb.GetCurrentState(serviceState));
+	_printf(_L("\nSession 1: "));
+	PrintUsbServiceState(serviceState);
+
+	LEAVEIFERROR(TheSecondUsbSession.GetCurrentState(serviceState));
+	_printf(_L("Session 2: "));
+	PrintUsbServiceState(serviceState);
+
+	TheSecondUsbSession.Close();
+	}
+
+// Concurrency test case (3.9.5 in test spec). One client starts USB while it's being
+// started by another one.
+void StartBWhileAStartingL()
+	{
+	LEAVEIFERROR(TheSecondUsbSession.Connect());
+
+	TRequestStatus status1;
+	TRequestStatus status2;
+	TUsbServiceState serviceState;
+
+	// Start on client 1.
+	TheUsb.Start(status1);
+
+	// Start on client 2.
+	TheSecondUsbSession.Start(status2);
+
+	// Wait for first request to complete.
+	User::WaitForRequest(status1);
+	LEAVEIFERROR(status1.Int());
+
+	// Wait for second request to complete.
+	User::WaitForRequest(status2);
+	LEAVEIFERROR(status2.Int());
+
+	// Get status on both sessions.
+
+	LEAVEIFERROR(TheUsb.GetCurrentState(serviceState));
+	_printf(_L("\nSession 1: "));
+	PrintUsbServiceState(serviceState);
+
+	LEAVEIFERROR(TheSecondUsbSession.GetCurrentState(serviceState));
+	_printf(_L("Session 2: "));
+	PrintUsbServiceState(serviceState);
+
+	TheSecondUsbSession.Close();
+	}
+
+#ifndef HURRICANE
+
+// Concurrency test case (3.9.3 in test spec). One client starts USB while it's being
+// stopped by another one.
+void StartBWhileAStoppingL()
+	{
+	LEAVEIFERROR(TheSecondUsbSession.Connect());
+
+	TRequestStatus status1;
+	TRequestStatus status2;
+	TUsbServiceState serviceState;
+
+	// Stop on client 1.
+	TheUsb.Stop(status1);
+
+	// Start on client 2.
+	TheSecondUsbSession.Start(status2);
+
+	// Wait for first request to complete.
+	User::WaitForRequest(status1);
+	if (status1.Int() != KErrUsbServiceStarted)
+		LEAVEIFERROR(status1.Int());
+
+	// Wait for second request to complete.
+	User::WaitForRequest(status2);
+	LEAVEIFERROR(status2.Int());
+
+	// Get status on both sessions.
+
+	LEAVEIFERROR(TheUsb.GetCurrentState(serviceState));
+	_printf(_L("\nSession 1: "));
+	PrintUsbServiceState(serviceState);
+
+	LEAVEIFERROR(TheSecondUsbSession.GetCurrentState(serviceState));
+	_printf(_L("Session 2: "));
+	PrintUsbServiceState(serviceState);
+
+	TheSecondUsbSession.Close();
+	}
+
+// Concurrency test case (3.9.4 in test spec). One client stops USB while it's being
+// stopped by another one.
+void StopBWhileAStoppingL()
+	{
+	LEAVEIFERROR(TheSecondUsbSession.Connect());
+
+	TRequestStatus status1;
+	TRequestStatus status2;
+	TUsbServiceState serviceState;
+
+	// Stop on client 1.
+	TheUsb.Stop(status1);
+
+	// Stop on client 2.
+	TheSecondUsbSession.Stop(status2);
+
+	// Wait for first request to complete.
+	User::WaitForRequest(status1);
+	LEAVEIFERROR(status1.Int());
+
+	// Wait for second request to complete.
+	User::WaitForRequest(status2);
+	LEAVEIFERROR(status2.Int());
+
+	// Get status on both sessions.
+
+	LEAVEIFERROR(TheUsb.GetCurrentState(serviceState));
+	_printf(_L("\nSession 1: "));
+	PrintUsbServiceState(serviceState);
+
+	LEAVEIFERROR(TheSecondUsbSession.GetCurrentState(serviceState));
+	_printf(_L("Session 2: "));
+	PrintUsbServiceState(serviceState);
+
+	TheSecondUsbSession.Close();
+	}
+/*
+void RegisterAsPrimaryClientL()
+	{
+	LEAVEIFERROR(TheUsb.RegisterAsPrimarySession());
+	ThePrimaryClient = ETrue;
+	}
+
+void DeregisterAsPrimaryClientL()
+	{
+	LEAVEIFERROR(TheUsb.DeregisterAsPrimarySession());
+	ThePrimaryClient = EFalse;
+	}
+*/
+void DbgCheckHeapL()
+	{
+	_printf(_L("Expected number of allocated heap cells: "));
+
+	TBuf<20> numBuf;
+	ReadString(numBuf);
+
+	TLex lex(numBuf);
+	TInt expected;
+	
+	if (lex.Val(expected) != KErrNone)
+		expected = 0;
+
+	LEAVEIFERROR(TheUsb.__DbgCheckHeap(expected));
+	}
+
+void DbgMarkEndL()
+	{
+	_printf(_L("Expected number of allocated heap cells: "));
+
+	TBuf<20> numBuf;
+	ReadString(numBuf);
+
+	TLex lex(numBuf);
+	TInt expected;
+	
+	if (lex.Val(expected) != KErrNone)
+		expected = 0;
+
+	LEAVEIFERROR(TheUsb.__DbgMarkEnd(expected));
+	}
+
+void DbgFailNextL()
+	{
+	_printf(_L("Number of allocation which will fail: "));
+
+	TBuf<20> numBuf;
+	ReadString(numBuf);
+
+	TLex lex(numBuf);
+	TInt allocNumber;
+	
+	if (lex.Val(allocNumber) != KErrNone)
+		allocNumber = 1;
+
+	LEAVEIFERROR(TheUsb.__DbgFailNext(allocNumber));
+	}
+
+#endif // HURRICANE
+
+void SetUsbSerialNumberL()
+	{
+	//Get a number from user
+	TBuf16<KUsbStringDescStringMaxSize> serNum;
+	
+	_printf(_L("\nEnter new USB serial number:\n"));
+	ReadString(serNum);
+	if(serNum.Length()==0)
+		{
+		TInt r = RProperty::Delete(KUidSystemCategory,0x101FE1DB);
+		if(r!=KErrNotFound && r!=KErrNone)
+			{
+			User::Leave(r);
+			}
+		_printf(_L("Serial number un-published. Restart USB for change to take effect.\ne.g. Option 1 -> 1 (calls RUsb::Start)\n"));		
+		}
+	else
+		{
+		_printf(_L("\nSetting serial number to %S\n"),&serNum);	
+		//Publish the number. It will be read by CUsbDevice::SetUsbDeviceSettingsL
+		//The key used is the UID KUidUsbmanServer = 0x101FE1DB
+		//The size of the serial number value published is KUsbStringDescStringMaxSize. The
+		//USB spec doesn't give a specific max size for the serial number, it just says it is
+		//a string descriptor, hence it can have size KUsbStringDescStringMaxSize (252 characters).
+		TInt r = RProperty::Define(KUidSystemCategory,0x101FE1DB,RProperty::EText,KUsbStringDescStringMaxSize);
+		if(r!=KErrAlreadyExists && r!=KErrNone)
+			{
+			User::Leave(r);
+			}
+		r = RProperty::Set(KUidSystemCategory,0x101FE1DB,serNum);
+		User::LeaveIfError(r);
+
+		_printf(_L("Serial number published. Restart USB for change to take effect.\ne.g. Option 1 -> 1 (calls RUsb::Start)\n"));		
+		}
+	}
+
+////////////////////////////////////////////////////////////////////////////////
+// Menus etc. below this point
+////////////////////////////////////////////////////////////////////////////////
+
+TBool SelectStartingTestL(TChar aChar)
+	{
+	TBool result = ETrue;
+	
+	switch (aChar)
+		{
+	case '1': StartUsbL(); break;
+	case '2': CancelStartingUsbL(EFalse); break;
+	case '3': CancelStartingUsbL(ETrue); break;
+	case '4': StartupStressTestL(); break;
+	case 'q':
+	case 'Q':
+		result = EFalse;
+//		break; // commented out as it's unreachable
+	default:
+		_printf(_L("\nInvalid key\n"));
+		break;
+		}
+	return result;
+	}
+
+void StartingTestsL()
+	{
+	_printf(_L("\nStarting tests\n"));
+	_printf(_L("--------------\n"));
+	_printf(_L("Available tests:\n\n"));
+	_printf(_L("1. Start USB\n"));
+	_printf(_L("2. Cancel starting USB\n"));
+	_printf(_L("3. Stop USB while it's starting\n"));
+	_printf(_L("4. Startup stress test\n"));
+	_printf(_L("\nSelection (q for main menu): "));
+
+	TChar ch = (TChar) _getch();
+	_printf(_L("\n"));
+
+	TRAP_IGNORE(SelectStartingTestL(ch));
+	}
+
+TBool SelectStoppingTestL(TChar aChar)
+	{
+	TBool result = ETrue;
+	
+	switch (aChar)
+		{
+	case '1': StopUsbSynchronousL(); break;
+#ifndef HURRICANE
+	case '2': StopUsbAsynchronousL(); break;
+    case '3': CancelStoppingUsbL(EFalse); break; 
+    case '4': CancelStoppingUsbL(ETrue); break; 
+#endif // HURRICANE
+	case 'q':
+	case 'Q':
+		result = EFalse;
+//		break; // commented out as it's unreachable
+	default:
+		_printf(_L("\nInvalid key\n"));
+		break;
+		}
+	return result;
+	}
+
+void StoppingTestsL()
+	{
+	_printf(_L("\nStopping tests\n"));
+	_printf(_L("--------------\n"));
+	_printf(_L("Available tests:\n\n"));
+	_printf(_L("1. Stop USB synchronously\n"));
+#ifndef HURRICANE
+	_printf(_L("2. Stop USB asynchronously\n"));
+	_printf(_L("3. Cancel stopping USB\n"));
+	_printf(_L("4. Start USB whilst stopping\n"));
+#endif // HURRICANE
+	_printf(_L("\nSelection (q for main menu): "));
+
+	TChar ch = (TChar) _getch();
+	_printf(_L("\n"));
+
+	TRAP_IGNORE(SelectStoppingTestL(ch));
+	}
+
+TBool SelectConcurrencyTestL(TChar aChar)
+	{
+	TBool result = ETrue;
+	
+	switch (aChar)
+		{
+	case '1': StartAThenStopBL(); break;
+	case '2': StopBWhileAStartingL(); break;
+	case '3': StartBWhileAStartingL(); break;
+#ifndef HURRICANE
+	case '4': StartBWhileAStoppingL(); break;
+	case '5': StopBWhileAStoppingL(); break;
+#endif // HURRICANE
+	case 'q':
+	case 'Q':
+		result = EFalse;
+//		break; // commented out as it's unreachable
+	default:
+		_printf(_L("\nInvalid key\n"));
+		break;
+		}
+	return result;
+	}
+
+void ConcurrencyTestsL()
+	{
+	_printf(_L("\nConcurrency tests\n"));
+	_printf(_L("-----------------\n"));
+	_printf(_L("Available tests:\n\n"));
+	_printf(_L("1. Client A starts, then client B stops\n"));
+	_printf(_L("2. Client B stops while client A is starting\n"));
+	_printf(_L("3. Client B starts while client A is starting\n"));
+#ifndef HURRICANE
+	_printf(_L("4. Client B starts while client A is stopping\n"));
+	_printf(_L("5. Client B stops while client A is stopping\n"));
+#endif // HURRICANE
+	_printf(_L("\nSelection (q for main menu): "));
+
+	TChar ch = (TChar) _getch();
+	_printf(_L("\n"));
+
+	TRAP_IGNORE(SelectConcurrencyTestL(ch));
+	}
+
+void SanityTestL()
+/**
+ * Call each API. Just a quick way of sanity-checking the API.
+ */
+	{
+	_printf(_L("\nQuick sanity check of all APIs\n"));
+	
+	RUsb usb;
+	LEAVEIFERROR(usb.Connect());
+	usb.Version();
+	
+	TUsbDeviceState deviceState;
+	TRequestStatus deviceStateNotStat;
+	usb.StateNotification(0xFFFF, deviceState, deviceStateNotStat);
+	usb.StateNotificationCancel();
+	User::WaitForRequest(deviceStateNotStat);
+	if ( deviceStateNotStat != KErrCancel )
+		{
+		LEAVEIFERROR(__LINE__);
+		}
+
+	usb.Stop(); // get it into a known state
+
+	TUsbServiceState serviceState;
+	LEAVEIFERROR(usb.GetServiceState(serviceState));
+	if ( serviceState != EUsbServiceIdle )
+		{
+		LEAVEIFERROR(__LINE__);
+		}
+	TRequestStatus serviceStateNotStat;
+	usb.ServiceStateNotification(serviceState, serviceStateNotStat);
+	// Start & Stop & Cancels
+	TRequestStatus startStat;
+	usb.Start(startStat);
+	User::WaitForRequest(startStat);
+	LEAVEIFERROR(startStat.Int());
+	User::WaitForRequest(serviceStateNotStat);
+	LEAVEIFERROR(serviceStateNotStat.Int());
+	if ( serviceState != EUsbServiceStarting )
+		{
+		LEAVEIFERROR(__LINE__);
+		}
+	LEAVEIFERROR(usb.GetServiceState(serviceState));
+	if ( serviceState != EUsbServiceStarted )
+		{
+		LEAVEIFERROR(__LINE__);
+		}
+	usb.Stop();
+	usb.StartCancel();
+	usb.StopCancel();
+	TRequestStatus stopStat;
+	usb.Stop(stopStat);
+	User::WaitForRequest(stopStat);
+	LEAVEIFERROR(stopStat.Int());
+
+	// Service state
+	LEAVEIFERROR(usb.GetServiceState(serviceState));
+	if ( serviceState != EUsbServiceIdle )
+		{
+		LEAVEIFERROR(__LINE__);
+		}
+	LEAVEIFERROR(usb.GetCurrentState(serviceState));
+	if ( serviceState != EUsbServiceIdle )
+		{
+		LEAVEIFERROR(__LINE__);
+		}
+	usb.ServiceStateNotification(serviceState, serviceStateNotStat);
+	usb.ServiceStateNotificationCancel();
+	User::WaitForRequest(serviceStateNotStat);
+	if ( serviceStateNotStat != KErrCancel )
+		{
+		LEAVEIFERROR(__LINE__);
+		}
+	User::After(1000000);
+	// Device state
+	LEAVEIFERROR(usb.GetDeviceState(deviceState));		// need default for Cedar below
+	if ( deviceState != EUsbDeviceStateUndefined && deviceState != EUsbDeviceStateDefault )
+		{
+		LEAVEIFERROR(__LINE__);
+		}
+	usb.DeviceStateNotification(0xFFFF, deviceState, deviceStateNotStat);
+	usb.DeviceStateNotificationCancel();
+	User::WaitForRequest(deviceStateNotStat);
+	if ( deviceStateNotStat != KErrCancel )
+		{
+		LEAVEIFERROR(__LINE__);
+		}
+	usb.DeviceStateNotification(EUsbDeviceStateDefault, deviceState, deviceStateNotStat);
+	usb.Start(startStat);
+	User::WaitForRequest(startStat);
+	LEAVEIFERROR(startStat.Int());
+	User::WaitForRequest(deviceStateNotStat);
+	LEAVEIFERROR(deviceStateNotStat.Int());		  // need undefined for Cedar below
+	if ( deviceState != EUsbDeviceStateDefault && deviceState != EUsbDeviceStateUndefined )
+		{
+		LEAVEIFERROR(__LINE__);
+		}
+	User::After(1000000);
+	LEAVEIFERROR(usb.GetDeviceState(deviceState));	// need default & undefined & configured for Cedar below
+	if ( deviceState != EUsbDeviceStateSuspended && deviceState != EUsbDeviceStateDefault && deviceState != EUsbDeviceStateUndefined && deviceState != EUsbDeviceStateConfigured)
+		{
+		LEAVEIFERROR(__LINE__);
+		}
+	// Heap marking
+	LEAVEIFERROR(usb.__DbgMarkHeap());
+	LEAVEIFERROR(usb.__DbgCheckHeap(0));
+	LEAVEIFERROR(usb.__DbgMarkEnd(0));
+	// NB __DbgFailNext is not checked
+	usb.Stop();
+	usb.Close();
+	}
+
+#ifndef HURRICANE
+
+TBool SelectMemoryTestL(TChar aChar)
+	{
+	TBool result = ETrue;
+			
+	switch (aChar)
+		{
+	case '1': LEAVEIFERROR(TheUsb.__DbgMarkHeap()); break;
+	case '2': DbgCheckHeapL(); break;
+	case '3': DbgMarkEndL(); break;
+	case '4': DbgFailNextL(); break;
+	case 'q':
+	case 'Q':
+		result = EFalse;
+//		break; // commented out as it's unreachable
+	default:
+		_printf(_L("\nInvalid key\n"));
+		break;
+		}
+	return result;
+	}
+
+void MemoryTestsL()
+	{
+	_printf(_L("\nMemory tests\n"));
+	_printf(_L("------------\n"));
+	_printf(_L("Available tests:\n\n"));
+	_printf(_L("1. Mark heap\n"));
+	_printf(_L("2. Check heap\n"));
+	_printf(_L("3. End marking heap\n"));
+	_printf(_L("4. Simulate allocation failure\n"));
+	_printf(_L("\nSelection (q for main menu): "));
+
+	TChar ch = (TChar) _getch();
+	_printf(_L("\n"));
+
+	TRAP_IGNORE(SelectMemoryTestL(ch));
+	}
+
+#endif // HURRICANE
+
+TBool SelectTestL(TChar aChar)
+	{
+	TBool result = ETrue;
+			
+	switch (aChar)
+		{
+	case '1': StartingTestsL(); break;
+	case '2': StoppingTestsL(); break;
+	case '3': MonitorUsbDeviceStateL(); break;
+	case '4': ConcurrencyTestsL(); break;
+	case '5': SetUsbSerialNumberL(); break;
+	case 's': case 'S': SanityTestL(); break;
+#ifndef HURRICANE
+/*	case 'r': RegisterAsPrimaryClientL(); break;
+	case 'd': DeregisterAsPrimaryClientL(); break;
+*/	case 'm': MemoryTestsL(); break;
+#endif // HURRICANE
+	case 'c': case 'C': CloseUsbL(); break;
+	case 'o': case 'O': OpenUsbL(); break;
+	case 'q':
+	case 'Q':
+		result = EFalse;
+//		break; // commented out as it's unreachable
+	default:
+		_printf(_L("\nInvalid key\n"));
+		break;
+		}
+	return result;
+	}
+
+void mainL()
+	{
+	// Create the global timer. This is used throughout the code for timing services.
+	TheTimer.CreateLocal();
+
+	TInt ret;
+
+#ifndef __WINS__
+	ret = User::LoadLogicalDevice(KUsbLddName);
+	if ((ret != KErrNone) && (ret != KErrAlreadyExists))
+		LEAVE(ret);
+
+	_printf(_L("Loaded USB LDD\n"));
+#endif
+
+	ret = StartC32();
+	if ((ret != KErrNone) && (ret != KErrAlreadyExists))
+		LEAVE(ret);
+
+	_printf(_L("Started C32\n"));
+
+	OpenUsbL();
+
+	TUsbServiceState serviceState;
+
+	TBool noExit = ETrue;
+	while (noExit)
+		{
+		_printf(_L("\nMain menu\n"));
+		_printf(_L("---------\n"));
+		_printf(_L("Available tests:\n\n"));
+		_printf(_L("1. Starting tests\n"));
+		_printf(_L("2. Stopping tests\n"));
+		_printf(_L("3. Monitor USB device state\n"));
+		_printf(_L("4. Concurrency tests\n"));
+		_printf(_L("5. Publish Serial Number\n"));
+		_printf(_L("S. Sanity test- quickly calls all APIs to make sure they don't panic\n"));
+#ifndef HURRICANE
+/*		_printf(_L("R. Register as primary client\n"));
+		_printf(_L("D. Deregister as primary client\n"));
+*/		_printf(_L("M. Memory tests\n"));
+#endif // HURRICANE
+		_printf(_L("C. Close connection to USB Manager\n"));
+		_printf(_L("O. Connect to USB Manager\n"));
+
+		if (TheUsb.Handle())
+			{
+			_printf(_L("\nConnected to USB "));
+			if (ThePrimaryClient)
+				_printf(_L("as the primary client.\n"));
+			else
+				_printf(_L("as a non-primary client.\n"));
+
+			ret = TheUsb.GetCurrentState(serviceState);
+			if (ret == KErrNone)
+				PrintUsbServiceState(serviceState);
+			else
+				_printf(_L("Couldn't get USB service state (error %d).\n"), ret);
+			}
+		else
+			{
+			_printf(_L("\nNot connected to USB.\n"));
+			}
+
+		_printf(_L("\nSelection (q to quit): "));
+
+		TChar ch = (TChar) _getch();
+		_printf(_L("\n"));
+
+		TRAP_IGNORE(noExit = SelectTestL(ch));
+		}
+
+	TheCommServ.Close();
+	TheUsb.Close();
+	}
+
+void consoleMainL()
+	{
+	console=Console::NewL(_L("T_USBMAN"),TSize(KConsFullScreen,KConsFullScreen));
+	CleanupStack::PushL(console);
+	mainL();
+	_printf(_L("[ press any key ]"));
+	_getch();
+	CleanupStack::PopAndDestroy();
+	}
+
+GLDEF_C TInt E32Main()
+	{
+	__UHEAP_MARK;
+	CTrapCleanup* cleanupStack=CTrapCleanup::New();
+	TRAP_IGNORE(consoleMainL());
+	delete cleanupStack;
+	__UHEAP_MARKEND;
+	return 0;
+	}