--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/shostmassstorage/server/protocol/cscsiprotocol.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,809 @@
+// Copyright (c) 2008-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:
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+#include <e32base.h>
+
+#include "debug.h"
+#include "msdebug.h"
+#include "msctypes.h"
+#include "shared.h"
+#include "msgservice.h"
+
+#include "mscutils.h"
+
+#include "mtransport.h"
+#include "mprotocol.h"
+#include "tscsiclientreq.h"
+#include "tscsiprimarycmds.h"
+#include "tscsiblockcmds.h"
+
+#include "mblocktransferprotocol.h"
+#include "tblocktransfer.h"
+
+#include "tsbcclientinterface.h"
+#include "tspcclientinterface.h"
+#include "cmassstoragefsm.h"
+#include "cscsiprotocol.h"
+
+#include "usbmshostpanic.h"
+
+
+
+/**
+Create the CScsiProtocol object.
+
+@param aLun The LUN for the device represented by this object
+@param aTransport The transport interface
+@param aStatusPollingInterval The polling interval
+@return CScsiProtocol* A reference to the
+*/
+CScsiProtocol* CScsiProtocol::NewL(TLun aLun, MTransport& aTransport)
+ {
+ __MSFNSLOG
+ CScsiProtocol* r = new (ELeave) CScsiProtocol(aTransport);
+
+ CleanupStack::PushL(r);
+ r->ConstructL(aLun);
+ CleanupStack::Pop();
+ return r;
+ }
+
+void CScsiProtocol::ConstructL(TLun aLun)
+ {
+ __MSFNLOG
+ iFsm = CMassStorageFsm::NewL(*this);
+ iState = EDisconnected;
+ }
+
+
+CScsiProtocol::CScsiProtocol(MTransport& aTransport)
+: iSpcInterface(aTransport),
+ iSbcInterface(NULL)
+ {
+ __MSFNLOG
+ }
+
+
+CScsiProtocol::~CScsiProtocol()
+ {
+ __MSFNLOG
+ delete iFsm;
+ delete iSbcInterface;
+ }
+
+
+void CScsiProtocol::InitialiseUnitL()
+ {
+ __MSFNLOG
+
+ // A device may take time to mount the media. If the device fails attempt to
+ // retry the connection for a number of seconds
+ TInt retryCounter = 20;
+ do
+ {
+ retryCounter--;
+ iFsm->ConnectLogicalUnitL();
+ iState = iFsm->IsConnected() ? EConnected: EDisconnected;
+
+ if (iState == EConnected)
+ {
+ break;
+ }
+ User::After(1000 * 200); // 200 mS
+ }
+ while (retryCounter);
+ }
+
+
+void CScsiProtocol::UninitialiseUnitL()
+ {
+ __MSFNLOG
+ iFsm->DisconnectLogicalUnitL();
+ }
+
+TBool CScsiProtocol::IsConnected()
+ {
+ __MSFNLOG
+ return (iState == EConnected)? ETrue : EFalse;
+ }
+
+void CScsiProtocol::ReadL(TPos aPos,
+ TDes8& aBuf,
+ TInt aLength)
+ {
+ __MSFNLOG
+ if(!IsConnected())
+ User::Leave(KErrNotReady);
+ iSbcInterface->iBlockTransfer.ReadL(*this, aPos, aLength, aBuf);
+ }
+
+
+void CScsiProtocol::BlockReadL(TPos aPos, TDes8& aCopybuf, TInt aLen)
+ {
+ __MSFNLOG
+ __ASSERT_DEBUG(aPos % iSbcInterface->iBlockTransfer.BlockLength() == 0,
+ User::Panic(KUsbMsHostPanicCat, EBlockDevice));
+
+ const TInt blockLen = iSbcInterface->iBlockTransfer.BlockLength();
+ TInt len = aLen;
+
+ TInt64 lba = aPos / static_cast<TInt64>(blockLen);
+
+ if (I64HIGH(lba))
+ {
+ User::LeaveIfError(KErrOverflow);
+ }
+
+ TInt err = iSbcInterface->Read10L(I64LOW(lba), aCopybuf, len);
+ if (err)
+ {
+ __SCSIPRINT1(_L("READ(10) Err=%d"), err);
+ DoCheckConditionL();
+ User::LeaveIfError(KErrAbort);
+ }
+
+ // handle residue
+ while (len != aLen)
+ {
+ __SCSIPRINT2(_L("SCSI Read Residue 0x%x bytes read (0x%x)"), len, aLen);
+ __SCSIPRINT2(_L("Pos=0x%lx Len=0x%x"), aPos, aLen);
+
+ // read next block
+
+ // full blocks read in bytes
+ TInt bytesRead = len/blockLen * blockLen;
+ aPos += bytesRead;
+ aLen -= bytesRead;
+ len = aLen;
+
+ __SCSIPRINT3(_L("New Pos=0x%lx Len=0x%x (bytes read = %x)"),
+ aPos, aLen, bytesRead);
+
+ aCopybuf.SetLength(bytesRead);
+
+ // read rest of the block
+ TInt err = iSbcInterface->Read10L(aPos/blockLen, aCopybuf, len);
+ if (err)
+ {
+ DoCheckConditionL();
+ User::LeaveIfError(KErrAbort);
+ }
+ }
+ }
+
+
+void CScsiProtocol::WriteL(TPos aPosition,
+ TDesC8& aBuf,
+ TInt aLength)
+ {
+ __MSFNLOG
+ if(!IsConnected())
+ User::Leave(KErrNotReady);
+ iSbcInterface->iBlockTransfer.WriteL(*this, aPosition, aLength, aBuf);
+ }
+
+
+void CScsiProtocol::BlockWriteL(TPos aPos, TDesC8& aCopybuf, TUint aOffset, TInt aLen)
+ {
+ __MSFNLOG
+ __ASSERT_DEBUG(aPos % iSbcInterface->iBlockTransfer.BlockLength() == 0,
+ User::Panic(KUsbMsHostPanicCat, EBlockDevice));
+ const TInt blockLen = iSbcInterface->iBlockTransfer.BlockLength();
+ TInt len = aLen;
+ TInt err = iSbcInterface->Write10L(aPos/blockLen, aCopybuf, aOffset, len);
+ if (err)
+ {
+ DoCheckConditionL();
+ User::LeaveIfError(KErrAbort);
+ }
+
+ while (len != aLen)
+ {
+ // handle residue
+ __SCSIPRINT2(_L("SCSI Write Residue 0x%x bytes read (0x%x)"), len, aLen);
+ __SCSIPRINT2(_L("Pos=0x%lx Len=0x%x"), aPos, aLen);
+
+ // write next block
+
+ // full blocks written in bytes
+ TInt bytesWritten = len/blockLen * blockLen;
+ aPos += bytesWritten;
+ aLen -= bytesWritten;
+ len = aLen;
+ __SCSIPRINT2(_L("New Pos=0x%lx Len=0x%x"), aPos, aLen);
+
+ TPtrC8 buf = aCopybuf.Mid(bytesWritten);
+
+ // write rest of the block
+ TInt err = iSbcInterface->Write10L(aPos/blockLen, buf, aOffset, len);
+ if (err)
+ {
+ DoCheckConditionL();
+ User::LeaveIfError(KErrAbort);
+ }
+ }
+ }
+
+
+void CScsiProtocol::GetCapacityL(TCapsInfo& aCapsInfo)
+ {
+ __MSFNLOG
+ if (!IsConnected())
+ {
+ DoScsiReadyCheckEventL();
+ }
+
+ TLba lastLba;
+ TUint32 blockLength;
+
+ TInt err = iSbcInterface->ReadCapacity10L(lastLba, blockLength);
+ if (err)
+ {
+ if (err == KErrCommandFailed)
+ {
+ // Clear sense error
+ DoCheckConditionL();
+ }
+ User::LeaveIfError(KErrAbort);
+ }
+
+ // update iWriteProtect
+ err = MsModeSense10L();
+ if (err)
+ {
+ if (err == KErrCommandFailed)
+ {
+ // Clear sense error
+ DoCheckConditionL();
+ }
+
+ err = MsModeSense6L();
+ if (err == KErrCommandFailed)
+ {
+ // Clear sense error
+ DoCheckConditionL();
+ }
+ }
+
+ aCapsInfo.iNumberOfBlocks = lastLba + 1;
+ aCapsInfo.iBlockLength = blockLength;
+ aCapsInfo.iWriteProtect = iWriteProtect;
+
+ __SCSIPRINT3(_L("numBlock = x%x , blockLength = %x wp = %d"),
+ lastLba + 1, blockLength, iWriteProtect);
+ }
+
+
+/**
+Perform SCSI INQUIRY command. The function leaves if the device response is not
+compliant with the protocol standard.
+
+@return TInt KErrNone if successful otherwise KErrCommandFailed to indicate a
+device status error
+*/
+TInt CScsiProtocol::MsInquiryL()
+ {
+ __MSFNLOG
+ ResetSbc();
+
+ /**
+ INQUIRY
+ */
+
+ /**
+ SPC states
+ - the INQUIRY data should be returned even though the device server is not
+ ready for other commands.
+
+ - If the standard INQUIRY data changes for any reason, the device server
+ shall generate a unit attention condition
+ */
+ TPeripheralInfo info;
+ TInt err = iSpcInterface.InquiryL(info);
+ if (err)
+ {
+ // KErrCommandFailed
+ return err;
+ }
+
+ // print reponse
+ __TESTREPORT1(_L("RMB = %d"), info.iRemovable);
+ __TESTREPORT2(_L("PERIPHERAL DEVICE TYPE = %d PQ = %d"),
+ info.iPeripheralDeviceType,
+ info.iPeripheralQualifier);
+ __TESTREPORT1(_L("VERSION = %d"), info.iVersion);
+ __TESTREPORT1(_L("RESPONSE DATA FORMAT = %d"), info.iResponseDataFormat);
+ __TESTREPORT3(_L("VENDOR ID %S PRODUCT ID %S REV %S"),
+ &info.iIdentification.iVendorId,
+ &info.iIdentification.iProductId,
+ &info.iIdentification.iProductRev);
+
+ if (info.iPeripheralQualifier != 0 && info.iPeripheralQualifier != 1)
+ {
+ __HOSTPRINT(_L("Peripheral Qualifier[Unknown device type]\n"))
+ return KErrNotSupported;
+ }
+
+ if (info.iPeripheralDeviceType != 0)
+ {
+ __HOSTPRINT(_L("Peripheral Device Type[Unsupported device type]\n"))
+ return KErrNotSupported;
+ }
+
+ iRemovableMedia = info.iRemovable;
+
+ // SCSI Block device
+ iSbcInterface = new (ELeave) TSbcClientInterface(iSpcInterface.Transport());
+
+ return KErrNone;
+ }
+
+
+/**
+Perform SCSI TEST UNIT READY command. The function leaves if the device response
+is not compliant with the protocol standard.
+
+@return TInt KErrNone if successful or otherwise KErrCommandFailed to indicate a
+device status error
+*/
+TInt CScsiProtocol::MsTestUnitReadyL()
+ {
+ __MSFNLOG
+ /* TestUnitReady */
+ return iSpcInterface.TestUnitReadyL();
+ }
+
+
+/**
+Perform SCSI READ CAPACITY (10) command. The function leaves if the device
+response is not compliant with the protocol standard.
+
+Before a block device can be read or written the media's capacity (LAST LBA and
+BLOCK SIZE) must be obtained. This function is used to initialise TBlockTransfer
+with the capacity parameters via TSbcInterface::ReadCapcaityL().
+
+@return TInt KErrNone if successful, KErrCommandFailed to indicate a
+device status error, KErrCommandStalled to indicate a device stall
+*/
+TInt CScsiProtocol::MsReadCapacityL()
+ {
+ __MSFNLOG
+ // READ CAPACITY
+ TUint32 blockSize;
+ TUint32 lastLba;
+ TInt err = iSbcInterface->ReadCapacity10L(lastLba, blockSize);
+
+ __TESTREPORT2(_L("CAPACITY: Block Size=0x%x Last LBA=0x%x"), blockSize, lastLba);
+ return err;
+ }
+
+
+/**
+Perform MODE SENSE (10) command. The function leaves if the device response is
+not compliant with the protocol standard.
+
+@return TInt KErrNone if successful, KErrCommandFailed to indicate a
+device status error, KErrCommandStalled to indicate a device stall
+*/
+TInt CScsiProtocol::MsModeSense10L()
+ {
+ __MSFNLOG
+ TBool writeProtected;
+ TInt err = iSbcInterface->ModeSense10L(TSbcClientInterface::EReturnAllModePages, writeProtected);
+
+ if (!err)
+ {
+ iWriteProtect = writeProtected;
+ }
+ return err;
+ }
+
+
+/**
+Perform SCSI MODE SENSE (6) command. The function leaves if the device response
+is not compliant with the protocol standard.
+
+@return TInt KErrNone if successful, KErrCommandFailed to indicate a
+device status error, KErrCommandStalled to indicate a device stall
+*/
+TInt CScsiProtocol::MsModeSense6L()
+ {
+ __MSFNLOG
+ TBool writeProtected;
+ TInt err = iSbcInterface->ModeSense6L(TSbcClientInterface::EReturnAllModePages, writeProtected);
+
+ if (!err)
+ {
+ iWriteProtect = writeProtected;
+ }
+ return err;
+ }
+
+
+/**
+Perform SCSI START STOP UNIT command. The function leaves if the device response
+is not compliant with the protocol standard.
+
+@return TInt KErrNone if successful otherwise KErrCommandFailed to indicate a
+device status error
+*/
+TInt CScsiProtocol::MsStartStopUnitL(TBool aStart)
+ {
+ __MSFNLOG
+ return iSbcInterface->StartStopUnitL(aStart);
+ }
+
+
+/**
+Perform SCSI PREVENT ALLOW MEDIA REMOVAL command. The function leaves if the
+device response is not compliant with the protocol standard.
+
+@return TInt KErrNone if successful otherwise KErrCommandFailed to indicate a
+device status error
+*/
+TInt CScsiProtocol::MsPreventAllowMediaRemovalL(TBool aPrevent)
+ {
+ __MSFNLOG
+ return iSpcInterface.PreventAllowMediumRemovalL(aPrevent);
+ }
+
+
+void CScsiProtocol::DoCheckConditionL()
+ {
+ __MSFNLOG
+ User::LeaveIfError(MsRequestSenseL());
+
+ // Check if init is needed
+ if (iSenseInfo.iSenseCode == TSenseInfo::ENotReady &&
+ iSenseInfo.iAdditional == TSenseInfo::EAscLogicalUnitNotReady &&
+ iSenseInfo.iQualifier == TSenseInfo::EAscqInitializingCommandRequired)
+ {
+ // start unit
+ TInt err = iSbcInterface->StartStopUnitL(ETrue);
+
+ if (err)
+ {
+ User::LeaveIfError(MsRequestSenseL());
+ }
+
+ }
+
+ TInt r = GetSystemWideSenseError(iSenseInfo);
+
+ if (((r == KErrNotReady) && (iState == EConnected)) ||
+ r == KErrDisconnected)
+ {
+ CompleteNotifyChangeL();
+ }
+ }
+
+
+/**
+Map SCSI sense error to a system wide error code
+KErrNotReady could happen due to any of the following reasons:
+ 1. Lun is in the process of becoming ready
+ 2. Initialising command is required
+ 3. Lun is not ready to process the command - meaning it is still handling
+ the previous command
+
+KErrUnknown could happen due to any of the following reasons:
+ 1. Mass storage device does not respond to the selected logical unit, other
+ than the locial unit not ready scenario 2. The command sent was not
+ recognized or contains a invalid code 3. Invialid field in the command block
+ 4. The requested logical unit is not supported
+ 5. The mass storage device cnosists of insufficient resource
+ 6. Hardware error
+ 7. Blank check
+ 8. Vendor specific error
+ 9. Any illegal request (we assume the commands sent by MSC should be
+ supported by the device, if not then the illegal request is said to be of
+ unknown system wide error for Symbian. 10. Miscompare - we do not support
+ the compare operation/command so this error should not happen
+
+KErrAccessDenied could happen due to any of the following reasons:
+ 1. Data protection error happened
+ 2. Media was write protected
+ 3. Media was not present
+
+KErrOverflow could happen due to any of the following reasons:
+ 1. Data over flow occured
+ 2. The requested LBA is out of range
+
+KErrAbort could happen due to any of the following reasons:
+ 1. A copy operation is aborted.
+ 2. The command is aborted
+
+KErrCorrupt could happen due to any of the following reasons:
+ 1. The underlying media was having errors
+
+KErrDisconnected could happen due to any of the following reasons:
+ 1. The media was changed/removed - While this error is happening the file
+ extension will be notified setting the iChanged flag
+*/
+TInt CScsiProtocol::GetSystemWideSenseError(const TSenseInfo& aSenseInfo)
+ {
+ __MSFNLOG
+ TInt ret = KErrNone;
+ TInt additionalError = KErrNone;
+
+ switch(aSenseInfo.iSenseCode)
+ {
+ case TSenseInfo::ENoSense:
+ case TSenseInfo::ERecoveredError:
+ ret = KErrNone;
+ break;
+ case TSenseInfo::ENotReady:
+ ret = KErrNotReady;
+ additionalError = ProcessAsCodes(aSenseInfo);
+ if (additionalError != KErrNone)
+ {
+ ret = additionalError;
+ }
+ break;
+ case TSenseInfo::EMediumError:
+ ret = KErrCorrupt;
+ additionalError = ProcessAsCodes(aSenseInfo);
+ if (additionalError != KErrNone)
+ {
+ ret = additionalError;
+ }
+ break;
+ case TSenseInfo::EUnitAttention:
+ ret = KErrDisconnected;
+ break;
+ case TSenseInfo::EDataProtection:
+ ret = KErrAccessDenied;
+ break;
+ case TSenseInfo::EIllegalRequest:
+ case TSenseInfo::EHardwareError:
+ case TSenseInfo::EBlankCheck:
+ case TSenseInfo::EVendorSpecific:
+ case TSenseInfo::EMisCompare:
+ ret = KErrUnknown;
+ break;
+ case TSenseInfo::ECopyAborted:
+ case TSenseInfo::EAbortedCommand:
+ ret = KErrAbort;
+ break;
+ case TSenseInfo::EDataOverflow:
+ ret = KErrOverflow;
+ break;
+ default:
+ ret = KErrUnknown;
+ break;
+ }
+
+ return ret;
+ }
+
+
+TInt CScsiProtocol::ProcessAsCodes(const TSenseInfo& aSenseInfo)
+ {
+ __MSFNLOG
+ TInt ret = KErrNone;
+
+ switch(aSenseInfo.iAdditional)
+ {
+ case TSenseInfo::EAscLogicalUnitNotReady:
+ case TSenseInfo::EMediaNotPresent:
+ ret = KErrNotReady;
+ break;
+
+ case TSenseInfo::ELbaOutOfRange:
+ ret = KErrOverflow;
+ break;
+
+ case TSenseInfo::EWriteProtected:
+ ret = KErrAccessDenied;
+ break;
+
+ case TSenseInfo::ENotReadyToReadyChange:
+ ret = KErrNone;
+ break;
+
+ case TSenseInfo::EAscLogicalUnitDoesNotRespondToSelection:
+ case TSenseInfo::EInvalidCmdCode:
+ case TSenseInfo::EInvalidFieldInCdb:
+ case TSenseInfo::ELuNotSupported:
+ case TSenseInfo::EInsufficientRes:
+ ret = KErrUnknown;
+ break;
+ default:
+ ret = KErrNone;
+ break;
+ }
+ return ret;
+ }
+
+
+/**
+Perform SCSI REQUEST SENSE command. The function leaves if the device response
+is not compliant with the protocol standard.
+
+@return TInt KErrNone if successful otherwise KErrCommandFailed to indicate a
+device status error
+*/
+TInt CScsiProtocol::MsRequestSenseL()
+ {
+ __MSFNLOG
+ return iSpcInterface.RequestSenseL(iSenseInfo) ? KErrCommandFailed : KErrNone;
+ }
+
+
+void CScsiProtocol::CreateSbcInterfaceL(TUint32 aBlockLen, TUint32 aLastLba)
+ {
+ __MSFNLOG
+ // SCSI Block device
+ ASSERT(iSbcInterface == NULL);
+ iSbcInterface = new (ELeave) TSbcClientInterface(iSpcInterface.Transport());
+ iSbcInterface->SetCapacity(aBlockLen, aLastLba);
+ }
+
+
+void CScsiProtocol::ResetSbc()
+ {
+ __MSFNLOG
+ if (iSbcInterface)
+ {
+ delete iSbcInterface;
+ iSbcInterface = NULL;
+ }
+ }
+
+
+void CScsiProtocol::NotifyChange(const RMessage2& aMessage)
+ {
+ __MSFNLOG
+ iMediaChangeNotifier.Register(aMessage);
+ }
+
+
+void CScsiProtocol::ForceCompleteNotifyChangeL()
+ {
+ __MSFNLOG
+ iMediaChangeNotifier.DoNotifyL();
+ }
+
+
+void CScsiProtocol::CancelChangeNotifierL()
+ {
+ __MSFNLOG
+ iMediaChangeNotifier.DoCancelL();
+ }
+
+
+void CScsiProtocol::SuspendL()
+ {
+ __MSFNLOG
+ if (iFsm->StartStopUnitRequired())
+ {
+ iSbcInterface->StartStopUnitL(EFalse);
+ }
+ }
+
+void CScsiProtocol::ResumeL()
+ {
+ __MSFNLOG
+ if (iFsm->StartStopUnitRequired())
+ {
+ iSbcInterface->StartStopUnitL(ETrue);
+ }
+ }
+
+
+void CScsiProtocol::DoScsiReadyCheckEventL()
+ {
+ __MSFNLOG
+ TInt err = KErrNone;
+
+ if(iFsm->IsRemovableMedia() || iState == EDisconnected)
+ {
+ iFsm->SetStatusCheck();
+ TRAP(err, iFsm->ConnectLogicalUnitL());
+ iFsm->ClearStatusCheck();
+
+ User::LeaveIfError(err);
+ err = iFsm->IsConnected() ? KErrNone : KErrNotReady;
+ }
+
+ if (iState == EConnected)
+ {
+ if (err != KErrNone)
+ {
+ iState = EDisconnected;
+ __SCSIPRINT(_L("** Disconnected Notification **"));
+ iMediaChangeNotifier.DoNotifyL();
+ }
+ }
+ else
+ {
+ if (err == KErrNone)
+ {
+ iState = EConnected;
+ __SCSIPRINT(_L("** Connected Notification **"));
+ iMediaChangeNotifier.DoNotifyL();
+ }
+ }
+ }
+
+void CScsiProtocol::CompleteNotifyChangeL()
+ {
+ __MSFNLOG
+ if (!iFsm->IsStatusCheck())
+ {
+ if (iState == EConnected)
+ {
+ iState = EDisconnected;
+ iMediaChangeNotifier.DoNotifyL();
+ }
+ }
+ }
+
+RMediaChangeNotifier::RMediaChangeNotifier()
+: iRegistered(EFalse)
+ {
+ __MSFNSLOG
+ }
+
+
+RMediaChangeNotifier::~RMediaChangeNotifier()
+ {
+ __MSFNSLOG
+ if (iRegistered)
+ iNotifier.Complete(KErrDisconnected);
+ }
+
+/**
+Initialise notifier to enable media change notfications.
+
+@param aMessage The message to commplete the notification
+*/
+void RMediaChangeNotifier::Register(const RMessage2& aMessage)
+ {
+ __MSFNLOG
+ iRegistered = ETrue;
+ iNotifier = aMessage;
+ }
+
+
+void RMediaChangeNotifier::DoNotifyL()
+ {
+ __MSFNLOG
+ CompleteNotifierL(KErrNone);
+ }
+
+void RMediaChangeNotifier::DoCancelL()
+ {
+ __MSFNLOG
+ CompleteNotifierL(KErrCancel);
+ }
+
+void RMediaChangeNotifier::CompleteNotifierL(TInt aReason)
+ {
+ __MSFNLOG
+ if (iRegistered)
+ {
+ TBool mediaChanged = ETrue;
+ TPtrC8 pStatus((TUint8*)&mediaChanged,sizeof(TBool));
+ iNotifier.WriteL(0,pStatus);
+ iNotifier.Complete(aReason);
+ iRegistered = EFalse;
+ }
+ }