diff -r 000000000000 -r a41df078684a userlibandfileserver/fileserver/shostmassstorage/server/protocol/cscsiprotocol.cpp --- /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 + +#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(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; + } + }