diff -r 000000000000 -r a41df078684a kernel/eka/drivers/pbus/mmc/session.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/drivers/pbus/mmc/session.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,690 @@ +// Copyright (c) 1999-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: +// + +#include + + +// -------- class DMMCSession -------- + +EXPORT_C DMMCSession::DMMCSession(const TMMCCallBack& aCallBack) +/** + * Constructor - initializes callbacks and timers. + * Once the session has been engaged, the completion of the request is signalled by calling + * the function provided in aCallback. A session will be completed in this way if it has completed + * normally, an error has occurred or the session has been stopped by this or another client. + * @param aCallBack reference to a TMMCCallback object to be called upon completion. + */ + : iCallBack(aCallBack), +#ifdef __EPOC32__ + iPollTimer(DMMCSession::PollTimerCallBack, this), + iRetryTimer(DMMCSession::RetryTimerCallBack, this), + iProgramTimer(DMMCSession::ProgramTimerCallBack, this), +#endif // #ifdef __EPOC32__ + iConfig() + { + } + +EXPORT_C DMMCSession::~DMMCSession() +/** + * Destructor. + */ + { + // Ensure that the stack isn't currently running in another thread's context, otherwise this session won't be + // removed from the stack's workset until some time later - by which time the session will have been deleted + __ASSERT_ALWAYS(!iStackP->StackRunning(), DMMCSocket::Panic(DMMCSocket::EMMCNotInDfcContext)); + Abort(); + UnlockStack(); + } + +EXPORT_C void DMMCSession::SetCard(TMMCard* aCardP) +/** + * Assigns a card to the session. The card pointer would normally be obtained via a call of DMMCStack::CardP(). + * Assigning a card to the session is the means by which a particular card in the stack is targeted for a + * particular request. Some requests involve broadcasting to the entire stack. However, the majority involve + * an individual card at some stage of the process and so an attempt to engage the session before a card has + * been assigned to it will generally fail straight away. It is possible to change the card assigned to the + * session as long as this is not attempted while the session is engaged. + * @param aCardP A pointer to the card to be assigned to the session. + */ + { + iCardP = aCardP; + iCID = iCardP->CID(); + } + +EXPORT_C void DMMCSession::SetupCIMReadBlock(TMMCArgument aDevAddr, TUint32 aLength, TUint8* aMemoryP) +/** + * Sets the session up to perform the CIM_READ_BLOCK macro as outlined by the MMCA. + * Having set-up the session for this operation, the client must then engage the session before the operation can commence. + * The CIM_READ_BLOCK macro reads a single block from the card. It starts by setting the block length (CMD16) as specified + * in 'aLength'. It then reads a single block of data (CMD17) from the card at offset 'aDevAddr' on the card into system + * memory starting at address 'aMemoryP'. + * @param aDevAddr Contains offset to the block to be read from the card + * @param aLength Block length + * @param aMemoryP host destination address + */ + { + ResetCommandStack(); + FillCommandArgs(aDevAddr, aLength, aMemoryP, aLength); + iSessionID = ECIMReadBlock; + } + +EXPORT_C void DMMCSession::SetupCIMWriteBlock(TMMCArgument aDevAddr, TUint32 aLength, TUint8* aMemoryP) +/** + * Set up the session to perform the CIM_WRITE_BLOCK macro as outlined by the MMCA. + * Having set-up the session for this operation, the client must then engage the session before the operation can commence. + * The CIM_WRITE_BLOCK macro writes a single block to the card. It starts by setting the block length (CMD16) as specified + * in 'aLength'. It then writes a single block of data (CMD24) to the card at offset 'aDevAddr' on the card reading from system + * memory starting at address 'aMemoryP'. + * @param aDevAddr Contains offset to the block to be written on the card + * @param aLength Block length + * @param aMemoryP Host source address + */ + { + ResetCommandStack(); + FillCommandArgs(aDevAddr, aLength, aMemoryP, aLength); + iSessionID = ECIMWriteBlock; + } + +EXPORT_C void DMMCSession::SetupCIMReadMBlock(TMMCArgument aDevAddr, TUint32 aLength, TUint8* aMemoryP, TUint32 aBlkLen) +/** + * Set up the session to perform the CIM_READ_MBLOCK macro as outlined by the MMCA. + * Having set-up the session for this operation, the client must then engage the session before the operation can commence. + * The CIM_READ_MBLOCK macro reads a series of blocks from the card. It starts by setting the block length (CMD16) as specified + * in 'aBlkLen'. It then issues the read multiple block command (CMD18) to continually transfer blocks from the card to host + * starting at offset 'aDevAddr' on the card into system memory starting at address 'aMemoryP'. This continues until 'aLength' + * bytes have been read at which point the Controller issues the stop command (CMD12) to halt the transfer. + * @param aDevAddr Contains offset to the block to be read from the card + * @param aLength Total number of bytes to read. + * @param aMemoryP Host destination address + * @param aBlkLen Block length + */ + { + ResetCommandStack(); + FillCommandArgs(aDevAddr, aLength, aMemoryP, aBlkLen); + iSessionID = ECIMReadMBlock; + } + +EXPORT_C void DMMCSession::SetupCIMWriteMBlock(TMMCArgument aDevAddr, TUint32 aLength, TUint8* aMemoryP, TUint32 aBlkLen) +/** + * Set up the session to perform the CIM_WRITE_MBLOCK macro as outlined by the MMCA. + * Having set-up the session for this operation, the client must then engage the session before the operation can commence. + * The CIM_WRITE_MBLOCK macro writes a series of blocks to the card. It starts by setting the block length (CMD16) as specified + * in 'aBlkLen'. It then issues the write multiple block command (CMD25) to continually transfer blocks from host to the card + * starting at address 'aMemoryP' in system memory and offset 'aDevAddr' on the card.. This continues until 'aLength' bytes have + * been written at which point the Controller issues the stop command (CMD12) to halt the transfer + * @param aDevAddr Contains offset to the block to be written on the card + * @param aLength Total number of bytes to write. + * @param aMemoryP Host source address + * @param aBlkLen Block length + */ + { + ResetCommandStack(); + FillCommandArgs(aDevAddr, aLength, aMemoryP, aBlkLen); + iSessionID = ECIMWriteMBlock; + } + +EXPORT_C void DMMCSession::SetupCIMEraseSector(TMMCArgument aDevAddr, TUint32 aLength) +/** + * Set up the session to perform the CIM_ERASE_SECTOR macro broadly as outlined by the MMCA. + * However, the macro only performs a sector erase of a contiguous area and doesn't support the un-tagging of particular sectors + * within the initial tagged area. Having set-up the session for this operation, the client must then engage the session before + * the operation can commence. + * The CIM_ERASE_SECTOR macro erases a range of sectors on the card starting at offset 'aDevAddr' on the card and ending at offset + * 'aDevAdd'+'aLength'. The entire area specified must lie within a single erase group. (The erase group size can be read from the CSD). + * The specified start offset and end offset need not coincide exactly with a sector boundary since the card will ignore LSBs below + * the sector size. The tag sector start command (CMD32) is first issued setting the address of the first sector to be erased. + * This is followed by the tag sector end command (CMD33) setting the address of the last sector to be erased. Now that the erase + * sectors are tagged, the erase command (CMD38) is sent followed by a send status command (CMD13) to read any additional status + * information from the card. + * @param aDevAddr Contains offset to the first block to be erased + * @param aLength Total number of bytes to erase + */ + { + ResetCommandStack(); + FillCommandArgs(aDevAddr, aLength, NULL, 0); + iSessionID = ECIMEraseSector; + } + +EXPORT_C void DMMCSession::SetupCIMEraseGroup(TMMCArgument aDevAddr, TUint32 aLength) +/** + * Set up the session to perform the CIM_ERASE_GROUP macro broadly as outlined by the MMCA. + * However, the macro only performs an erase group erase of a contiguous area and doesn't support the un-tagging of particular + * erase groups within the initial tagged area. Having set-up the session for this operation, the client must then engage the + * session before the operation can commence. + * The CIM_ERASE_GROUP macro erases a range of erase groups on the card starting at offset 'aDevAddr' on the card and ending at + * offset 'aDevAdd'+'aLength'. The specified start offset and end offset need not coincide exactly with an erase group boundary + * since the card will ignore LSBs below the erase group size. The tag ease group start command (CMD35) is first issued setting + * the address of the first erase group to be erased. This is followed by the tag erase group end command (CMD36) setting the + * address of the last erase group to be erased. Now that the erase groups are tagged, the erase command (CMD38) is sent followed + * by a send status command (CMD13) to read any additional status information from the card. + * @param aDevAddr Contains offset to the first block to be erased + * @param aLength Total number of bytes to erase + */ + { + ResetCommandStack(); + FillCommandArgs(aDevAddr, aLength, NULL, 0); + iSessionID = ECIMEraseGroup; + } + +EXPORT_C void DMMCSession::SetupCIMReadIO(TUint8 aRegAddr, TUint32 aLength, TUint8* aMemoryP) +/** + * Set up the session to perform the read i/o macro (CMD39). + * This macro reads a stream of bytes from an I/O register on a MultiMediaCard. This makes use of the fast i/o (CMD39) command, + * reading 'aLength' bytes of data from I/O register 'aRegAddr' on the card into system memory starting at address 'aMemoryP'. + * Having set-up the session for this operation, the client must then engage the session before the operation can commence. + * @param aRegAddr Address of IO register + * @param aLength Total number of bytes to read + * @param aMemoryP Host destination address + */ + { + ResetCommandStack(); + FillCommandArgs(aRegAddr, aLength, aMemoryP, 0); + iSessionID = ECIMReadIO; + } + +EXPORT_C void DMMCSession::SetupCIMWriteIO(TUint8 aRegAddr, TUint32 aLength, TUint8* aMemoryP) +/** + * Set up the session to perform the write i/o macro (CMD39). + * This macro writes a stream of bytes to an I/O register on a MultiMediaCard. This makes use of the fast i/o (CMD39) command, + * writing 'aLength' bytes of data to I/O register 'aRegAddr' on the card from system memory starting at address 'aMemoryP'. + * Having set-up the session for this operation, the client must then engage the session before the operation can commence. + * @param aRegAddr Address of IO register + * @param aLength Total number of bytes to write + * @param aMemoryP Host source address + */ + { + ResetCommandStack(); + FillCommandArgs(aRegAddr, aLength, aMemoryP, 0); + iSessionID = ECIMWriteIO; + } + +EXPORT_C void DMMCSession::SetupCIMLockUnlock(TUint32 aLength, TUint8* aMemoryP) +/** + * Set up the session to perform the lock-unlock macro (CMD42). + * This macro is used to manage the password protection feature (if supported) on a MultiMediaCard. + * This same macro is used to lock or unlock a card, set or clear a password or force erase a card. + * Having set-up the session for the required operation, the client must then engage the session before + * the operation can commence. + * The block length (CMD16) as specified in 'aLength' is first set. The lock unlock command (CMD42) is + * then issued. This command has the same structure as a regular single block write command. + * A data block is written to the card from system memory starting at address 'aMemoryP'. The transferred + * data block should contain the password setting mode, the password length and the password data if appropriate. + * @param aLength Block length + * @param aMemoryP Host source address containing password data + */ + { + __KTRACE_OPT(KPBUS1, Kern::Printf("ms:slu%08x", aLength)); + + ResetCommandStack(); + FillCommandDesc(ECmdLockUnlock); + FillCommandArgs(0, aLength, aMemoryP, aLength); + iSessionID = ECIMLockUnlock; + } + +EXPORT_C void DMMCSession::SetupCommand(TMMCCommandEnum aCommand, TMMCArgument anArgument) +/** + * Set up the session to issue a raw command to the card. + * This raw command function should be used when issuing a known command with or without an argument. + * Having set-up the session for this operation, the client must then engage this session before + * the operation can commence. + * @param aCommand Command to be sent + * @param anArgument Associated argument + */ + { + ResetCommandStack(); + FillCommandDesc(aCommand, anArgument); + iSessionID = ECIMNakedSession; + } + +EXPORT_C void DMMCSession::SetupRSCommand(TMMCCommandEnum aCommand, TMMCArgument anArgument, + TUint32 aResponseLength, TMMCCommandTypeEnum aCommandType, + TMMCResponseTypeEnum aResponseType, + TUint32 aCommandClass) +/** + * Set up the session to issue a raw command to the card. + * This raw command function should be used when issuing an unknown command, an argument and an unknown response type. + * Having set-up the session for this operation, the client must then engage this session before the operation can commence. + * @param aCommand + * @param anArgument + * @param aResponseLength + * @param aCommandType + * @param aResponseType + * @param aCommandClass + * @todo Complete the parameter descriptions + */ + { + ResetCommandStack(); + FillCommandDesc(aCommand, anArgument); + TMMCCommandSpec& cmdSpec = Command().iSpec; + cmdSpec.iDirection = EDirNone; + + if( aResponseLength <= KMMCMaxResponseLength ) + cmdSpec.iResponseLength = aResponseLength; + + if( aCommandType != ECmdTypeUK ) + cmdSpec.iCommandType = aCommandType; + + if( aResponseType != ERespTypeUnknown ) + cmdSpec.iResponseType = aResponseType; + + if( aCommandClass != KMMCCmdClassNone ) + cmdSpec.iCommandClass = aCommandClass; + + iSessionID = ECIMNakedSession; + } + +EXPORT_C void DMMCSession::SetupDTCommand(TMMCCommandEnum aCommand, TMMCArgument anArgument, + TUint32 aTotalLength, TUint8* aMemoryAddress, TUint32 aBlockLength, + TBool aStopTransmission, TMMCCmdDirEnum aDir, + TUint32 aCommandClass) +/** + * Set up the session to issue a raw command to the card. + * This raw command function should be used when issuing a generic transfer command and argument. + * Having set-up the session for this operation, the client must then engage this session before + * the operation can commence. + * @param aCommand + * @param anArgument + * @param aTotalLength + * @param aMemoryAddress + * @param aBlockLength + * @param aStopTransmission + * @param aDir + * @param aCommandClass + * @todo Complete the parameter descriptions + */ + { + ResetCommandStack(); + FillCommandDesc(aCommand); + FillCommandArgs(anArgument, aTotalLength, aMemoryAddress, aBlockLength); + TMMCCommandDesc& cmd = Command(); + + if( aBlockLength == 0 ) + cmd.iBlockLength = aTotalLength; + + cmd.iSpec.iMultipleBlocks = (cmd.iBlockLength != aTotalLength); + + if( aStopTransmission ) + cmd.iSpec.iUseStopTransmission = ETrue; + + if( aDir != EDirNone ) + { + cmd.iSpec.iUseStopTransmission = aStopTransmission; + cmd.iSpec.iDirection = aDir; + } + + if( aCommandClass != KMMCCmdClassNone ) + cmd.iSpec.iCommandClass = aCommandClass; + + iSessionID = ECIMNakedSession; + } + +void DMMCSession::SetupCIMControl(TInt aSessID) +// +// find matching macro function for supplied session +// + { + TMMCSMSTFunc f = GetMacro(aSessID); + + if (f == 0) + f = DMMCStack::NoSessionSMST; + + iSessionID = (TMMCSessionTypeEnum) aSessID; + iBytesTransferred = 0; + iMMCExitCode = KMMCErrNone; + iState = 0; + iInitContext = 0; + iGlobalRetries = 0; + iDoAbort = iDoStop = iDoComplete = EFalse; + iBlockOn = 0; + + ResetCommandStack(); + + iMachine.Setup(f, iStackP); + } + +EXPORT_C TMMCSMSTFunc DMMCSession::GetMacro(TInt aSessNum) const + { + TMMCSMSTFunc f = 0; + + static const TMMCSMSTFunc macros[KMMCMaxSessionTypeNumber] = + { + DMMCStack::NakedSessionSMST, + DMMCStack::CIMUpdateAcqSMST, + DMMCStack::CIMInitStackSMST, + DMMCStack::CIMCheckStackSMST, + DMMCStack::CIMSetupCardSMST, + DMMCStack::CIMReadWriteBlocksSMST, // CIMReadBlock + DMMCStack::CIMReadWriteBlocksSMST, // CIMWriteBlock + DMMCStack::CIMReadWriteBlocksSMST, // CIMReadMBlock + DMMCStack::CIMReadWriteBlocksSMST, // CIMWriteMBlock + DMMCStack::CIMEraseSMST, + DMMCStack::CIMEraseSMST, + DMMCStack::CIMReadWriteIOSMST, + DMMCStack::CIMReadWriteIOSMST, + DMMCStack::CIMLockUnlockSMST, // CIMLockUnlock + DMMCStack::NoSessionSMST, // CIMLockStack is never really executed as a session + DMMCStack::InitStackAfterUnlockSMST, + DMMCStack::CIMAutoUnlockSMST, + DMMCStack::ExecSleepCommandSMST // CIMSleep + }; + + if (aSessNum >= 0 && aSessNum < (TInt) KMMCMaxSessionTypeNumber) + f = macros[aSessNum]; + + return f; + } + +EXPORT_C TInt DMMCSession::Engage() +/** + * Enque this session for execution on the DMMCStack object which is serving it. + * @return KErrBadDriver if no stack is associated with the session + * @return KErrServerBusy if the stack is currently locked (and KMMCModeEnqueIfLocked flag is cleared) + * @return KErrNotReady if the media is not present + * @return KErrNone if successful + */ + { + __KTRACE_OPT(KPBUS1,Kern::Printf(">ms:eng")); + + if( iStackP == NULL ) + return( KErrBadDriver ); + + if( iStackP->iLockingSessionP != NULL && iStackP->iLockingSessionP != this && + (iStackP->EffectiveModes(iConfig) & KMMCModeEnqueIfLocked) == 0 ) + return( KErrServerBusy ); + + const TMediaState doorState=iStackP->MMCSocket()->iMediaChange->MediaState(); + + __KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:Eng ds = %x", doorState)); + + if (doorState == EDoorOpen) + return KErrNotReady; + + SetupCIMControl(iSessionID); + + iStackP->Add(this); + + __KTRACE_OPT(KPBUS1,Kern::Printf("IsPresent() && !(iState & KMMCSessStateCardIsGone) ) + return( iCardP->RCA() ); + return(0); + } + +#ifdef __EPOC32__ +void DMMCSession::ProgramTimerCallBack(TAny* aSessP) + { + __KTRACE_OPT(KPBUS1,Kern::Printf("=mss:pgtcb")); + + static_cast(aSessP)->iState |= KMMCSessStateDoDFC; + static_cast(aSessP)->UnBlock(KMMCBlockOnPgmTimer, KMMCErrNone); + } + +void DMMCSession::PollTimerCallBack(TAny* aSessP) + { + __KTRACE_OPT(KPBUS1,Kern::Printf("=mss:ptcb")); + + static_cast(aSessP)->iState |= KMMCSessStateDoDFC; + static_cast(aSessP)->UnBlock(KMMCBlockOnPollTimer, KMMCErrNone); + } + +void DMMCSession::RetryTimerCallBack(TAny* aSessP) + { + __KTRACE_OPT(KPBUS1,Kern::Printf("=mss:rtcb")); + + static_cast(aSessP)->iState |= KMMCSessStateDoDFC; + static_cast(aSessP)->UnBlock(KMMCBlockOnRetryTimer, KMMCErrNone); + } + +#endif // #ifdef __EPOC32__ + +EXPORT_C TInt DMMCSession::EpocErrorCode() const +/** + * Returns the last Symbian OS style error code returned in this session. + * The Symbian OS error code is derived from both the last MMC specific exit code MMCExitCode() + * and the last status information from the card (iLastStatus). + * @return Standard Symbian OS error code + */ + { + __KTRACE_OPT(KPBUS1,Kern::Printf("=mss:eee:%08x,%08x", MMCExitCode(), LastStatus().State() )); + + struct errorTableEntry + { + TUint32 iMask; + TInt iErrorCode; + }; + + static const errorTableEntry mmcTable[] = + { + {KMMCErrNotSupported, KErrNotSupported}, + {KMMCErrStackNotReady, KErrBadPower}, + {KMMCErrArgument, KErrArgument}, + {KMMCErrBrokenLock | KMMCErrPowerDown | KMMCErrAbort, KErrAbort}, + {KMMCErrNoCard | KMMCErrResponseTimeOut | KMMCErrDataTimeOut | + KMMCErrBusyTimeOut | KMMCErrBusTimeOut, KErrNotReady}, + {KMMCErrResponseCRC|KMMCErrDataCRC|KMMCErrCommandCRC, KErrCorrupt}, + {KMMCErrLocked, KErrLocked}, + {KMMCErrNotFound, KErrNotFound}, + {KMMCErrAlreadyExists, KErrAlreadyExists}, + {KMMCErrGeneral, KErrGeneral}, + {~0UL, KErrUnknown} + }; + + static const errorTableEntry statusTable[] = + { + {KMMCStatErrOverrun|KMMCStatErrUnderrun| + KMMCStatErrCardECCFailed|KMMCStatErrComCRCError, KErrGeneral}, + {KMMCStatErrCSDOverwrite|KMMCStatErrWPViolation, KErrWrite}, + {KMMCStatErrLockUnlock, KErrLocked}, + {KMMCStatErrIllegalCommand, KErrNotSupported}, + {KMMCStatErrEraseParam|KMMCStatErrEraseSeqError| + KMMCStatErrBlockLenError|KMMCStatErrAddressError| + KMMCStatErrOutOfRange, KErrArgument}, + {~0UL, KErrUnknown} + }; + + TUint32 errCode = MMCExitCode(); + + if( errCode == 0 ) + return KErrNone; + + const errorTableEntry* ptr = &mmcTable[0]; + + if( errCode == KMMCErrStatus ) + { + ptr = &statusTable[0]; + + if( (errCode = LastStatus()) == 0 ) + return( KErrUnknown ); + } + + for( ;; ) + if( (errCode & ptr->iMask) != 0 ) + return( ptr->iErrorCode ); + else + ptr++; + }