kerneltest/sdiotest/source/d_sdioif.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 15 Sep 2010 13:42:27 +0300
branchRCL_3
changeset 268 345b1ca54e88
permissions -rw-r--r--
Revision: 201036 Kit: 201036

// Copyright (c) 2003-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:
// LDD for testing SDIO functions
// 
//

#include <kernel/kernel.h>
#include "regifc.h"
#include "cisreader.h"
#include "d_sdioif.h"

/**
Define the name of the LDD.

@internal
@test
*/
_LIT(KLddName,"D_SDIOIF");

/**
Define the version of the LDD.

@internal
@test
*/
const TInt KMajorVersionNumber=1;
const TInt KMinorVersionNumber=0;
const TInt KBuildVersionNumber=1;

/**
Define the default socket number.

@internal
@test
*/
#ifdef __WINS__
	const TInt KSocketNumber = 0;
#else
	const TInt KSocketNumber = 0;
//	const TInt KSocketNumber = 1;	// 1 for Integrator!!
#endif

/**
Define the default stack number.

@internal
@test
*/
const TInt KStackNumber  = 0;

/**
Define the default card number.

@internal
@test
*/
const TInt KCardNumber   = 0;

/**
Define an invalid function number outside the normal 0-7 range.

@internal
@test
*/
const TUint8 KInvalidFuncNum = 8;

/**
Define the global Dfc Que.

@internal
@test
*/
TDynamicDfcQue* gDfcQ;

class DTestFactory : public DLogicalDevice
/**
Class to act as a factory for the test LDD

@internal
@test
*/
	{
public:
	DTestFactory();
	~DTestFactory();
	virtual TInt Install(); 					//overriding pure virtual
	virtual void GetCaps(TDes8& aDes) const;	//overriding pure virtual
	virtual TInt Create(DLogicalChannelBase*& aChannel); 	//overriding pure virtual
	};


class DTest : public DLogicalChannel
/**
Class containing the logical device driver to drive the SDIO classes.

@internal
@test
*/
	{
public:
	DTest();
	virtual ~DTest();
protected:
	virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
	virtual void HandleMsg(class TMessageBase *);
	virtual TInt SendMsg(TMessageBase* aMsg);
private:
	TInt SendRequest(TMessageBase* aMsg);
	TInt DoRequest(TInt aReqNo, TRequestStatus* aStatus, TAny* a1, TAny* a2);
	TInt SendControl(TMessageBase* aMsg);
	TInt DoControl(TInt aFunction, TAny* a1, TAny* a2);
	TInt CheckForChangeOfCis(TInt aFunc);
	TInt DoCancel(TUint aMask);
	static void EventCallBack(TAny* aPtr, TInt aReason, TAny* a1, TAny* a2);

private:
	// The SDIO objects
	DMMCSocket* iSocketP;
	DMMCStack*  iStackP;
	TSDIOCard*  iCardP;
    TInt iFunc;
	TCisReader iCisRd;

	DThread* iClient;
	TPBusCallBack iBusEventCallback;

	// Client requests used for creating local copies of user requests, WDP safe
	TClientRequest* 								iPowerUpRequest;
	TClientRequest* 								iResetCisRequest;
	TClientDataRequest<TSDIOCardConfig>*			iCardCommonReadRequest;
	TClientDataRequest<TSDIOFunctionCaps>*			iFunctionCapsRequest;
	TClientDataRequest<TUint>*						iReadDirectRequest; // Use TUint rather than TUint8 for alignment purposes
	};

DECLARE_STANDARD_LDD()
/**
The standard entry point for logical device drivers.

@internal
@test
*/
	{
	return new DTestFactory;
	}

DTestFactory::DTestFactory()
/**
Constructor.

@internal
@test
*/
	{
    iParseMask=KDeviceAllowUnit;
	iUnitsMask=0xffffffff;
	iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
	}

TInt DTestFactory::Create(DLogicalChannelBase*& aChannel)
/**
Create a new DTest on this logical device.

@return One of the system wide error codes.

@internal
@test
*/
	{
	aChannel=new DTest;
	return aChannel?KErrNone:KErrNoMemory;
	}

const TInt KDSdioIfThreadPriority = 27;
 _LIT(KDSdioIfThread,"DSdioIfThread");

TInt DTestFactory::Install()
/**
Install the LDD - overriding pure virtual.

@return One of the system wide error codes.

@internal
@test
*/
	{
	// Allocate a kernel thread to run the DFC 
	TInt r = Kern::DynamicDfcQCreate(gDfcQ, KDSdioIfThreadPriority, KDSdioIfThread);

	if (r != KErrNone)
		return r; 	

	return SetName(&KLddName);
	}

void DTestFactory::GetCaps(TDes8& aDes) const
/**
Return the capabilities of the LDD.

@return A packaged TCapsTestV01.

@internal
@test
*/
	{
	TCapsTestV01 b;
	b.iVersion=TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber);
    Kern::InfoCopy(aDes,(TUint8*)&b,sizeof(b));
	}

DTestFactory::~DTestFactory()
/**
Destructor

@internal
@test
*/
	{
	if (gDfcQ)
		gDfcQ->Destroy();
	}

TInt DTest::DoCreate(TInt aUnit, const TDesC8* /*aInfo*/, const TVersion& aVer)
/**
Create a logical channel.

@param aUnit The socket number.
@param aInfo Not used.
@param aVer The version requested.

@return KErrNone if the channel was created. KErrNotSupported if the version is not supported
        otherwise one of the system wide error codes.

@internal
@test
*/
	{

	if (!Kern::QueryVersionSupported(TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber),aVer))
		return KErrNotSupported;

	// Create the asynchronous callback client request
	TInt ret = Kern::CreateClientRequest(iPowerUpRequest);
	if (ret != KErrNone)
		return ret;

	ret = Kern::CreateClientRequest(iResetCisRequest);
	if (ret != KErrNone)
		return ret;
	
	ret = Kern::CreateClientDataRequest(iReadDirectRequest);
	if (ret != KErrNone)
		return ret;

	ret = Kern::CreateClientDataRequest(iCardCommonReadRequest);
	if (ret != KErrNone)
		return ret;

	ret = Kern::CreateClientDataRequest(iFunctionCapsRequest);
	if (ret != KErrNone)
		return ret;
	
	//
	// Obtain the appropriate card from the socket/stack
	//
	iSocketP = static_cast<DMMCSocket*>(DPBusSocket::SocketFromId(KSocketNumber));
	if(iSocketP == NULL)
		return KErrNoMemory;

	iStackP = static_cast<DSDIOStack*>(iSocketP->Stack(KStackNumber));
	if(iStackP == NULL)
		return KErrNoMemory;

	iCardP = static_cast<TSDIOCard*>(iStackP->CardP(KCardNumber));
	if(iCardP == NULL)
		return KErrNoMemory;

	iFunc=KInvalidFuncNum; // Indicates Cis reader isn't selected

	SetDfcQ(gDfcQ);
	iMsgQ.Receive();

	iBusEventCallback.SetSocket(aUnit);
	iBusEventCallback.Add();

	return KErrNone;
	}

DTest::DTest()
/**
Constructor.

@internal
@test
*/
	: iBusEventCallback(DTest::EventCallBack, this)
	{
	iClient=&Kern::CurrentThread();
	((DObject*)iClient)->Open();	// can't fail since thread is running
	}


DTest::~DTest()
/**
Destructor.

@internal
@test
*/
	{
	iBusEventCallback.Remove();
	
	// Destroy the client requests 
	Kern::DestroyClientRequest(iPowerUpRequest);
	Kern::DestroyClientRequest(iResetCisRequest);
	Kern::DestroyClientRequest(iReadDirectRequest);	
	Kern::DestroyClientRequest(iCardCommonReadRequest);
	Kern::DestroyClientRequest(iFunctionCapsRequest);
	
	Kern::SafeClose((DObject*&)iClient,NULL);
	}

/**
Pre-process the received message to prepare the client's request.

@param aMsg A pointer to a message (request) from the user side.
@return One of the system wide error codes.

@internal
@test
*/
TInt DTest::SendMsg(TMessageBase* aMsg)
	{
	TThreadMessage& m = *(TThreadMessage*)aMsg;
    TInt id = m.iValue;

	// we only support one client
	if (id != (TInt)ECloseMsg && m.Client() != iClient)
		return KErrAccessDenied;
	
	TInt r = KErrNone;
	if (id != (TInt)ECloseMsg && id != KMaxTInt)
		{
		if (id<0)
			{
			// It's a request
			TRequestStatus* pS = (TRequestStatus*)m.Ptr0();
			
			// Pre-process the request
			r = SendRequest(aMsg);
			if (r != KErrNone)
				Kern::RequestComplete(pS,r);
			}
		else
			{
			// Pre-process the control 
			r = SendControl(aMsg);
			}
		}
	else
		r = DLogicalChannel::SendMsg(aMsg);
	
	return r;
	}

/**
Handle a request message from the user side RSdioCardCntrlIf.

@param aMsg A pointer to a message (request) from the user side.

@internal
@test
*/
void DTest::HandleMsg(TMessageBase* aMsg)
    {
    TThreadMessage& m=*(TThreadMessage*)aMsg;
    TInt id=m.iValue;
    
	if (id==(TInt)ECloseMsg)
		{
	    // Check for a close message
		m.Complete(KErrNone, EFalse);
		return;
		}
    else if (id==KMaxTInt)
		{
		// DoCancel
		DoCancel(m.Int0());
		m.Complete(KErrNone, ETrue);
		return;
		}

    if (id<0)
		{
		// DoRequest
		TRequestStatus* pS=(TRequestStatus*)m.Ptr0();
		TInt r=DoRequest(~id, pS, m.Ptr1(), m.Ptr2());
		if (r!=KErrNone)
	    	Kern::RequestComplete(iClient, pS, r);
		m.Complete(KErrNone,ETrue);
		}
    else
		{
		// DoControl
		TInt r=DoControl(id,m.Ptr0(),m.Ptr1());
		m.Complete(r,ETrue);
		}
	}

/**
Handle a pre-process for a request message from the user side RSdioCardCntrlIf.
This will set-up the client requests.

@param aMsg A pointer to a message (request) from the user side.
@return One of the system wide error codes.

@internal
@test
*/
TInt DTest::SendRequest(TMessageBase* aMsg)
	{
	TThreadMessage& m = *(TThreadMessage*)aMsg;
    TInt function = ~m.iValue;
	TRequestStatus* pS = (TRequestStatus*)m.Ptr0();
	TAny* a2 = m.Ptr2();
		
	TInt r = KErrNotSupported;
	switch (function)
		{
		// A request to power up the SDIO card & stack
		case RSdioCardCntrlIf::EReqPwrUp:
			r = iPowerUpRequest->SetStatus(pS);
	       	if (r != KErrNone)
        		return r;
 			break;
		// A request to read generic data from the SDIO card 
        case RSdioCardCntrlIf::ERequestReadDirect:
           	{
            r = iReadDirectRequest->SetStatus(pS);
            if (r != KErrNone)
            	return r;
            iReadDirectRequest->SetDestPtr(a2);            
            }
    		break;			
    	// A request to reset the CIS pointer 
        case RSdioCardCntrlIf::ERequestResetCis:
			r = iResetCisRequest->SetStatus(pS);
			if (r != KErrNone)
				return r;
			break;
			
       	// A request to read the Card Common Config  
        case RSdioCardCntrlIf::ERequestGetCommonConfig:
        	{
        	r = iCardCommonReadRequest->SetStatus(pS);
        	if (r != KErrNone)
        		return r;
        	iCardCommonReadRequest->SetDestPtr(a2);
        	}
			break;
			
        // A request to read the function data (FBR)  
        case RSdioCardCntrlIf::ERequestGetFunctionConfig:
        	{
        	r = iFunctionCapsRequest->SetStatus(pS);
        	if (r != KErrNone)
        		return r;
        	iFunctionCapsRequest->SetDestPtr(a2);
        	}
			break;
		}

	if (r == KErrNone)
		r = DLogicalChannel::SendMsg(aMsg);
	return r;
	}

/**
Process any asynchronous requests from the user side.

@param aFunction The asynchronous function to invoke. 
@param aStatus On completion, the success code for the function.
@param a1 Context sensitive data.
@param a2 Context sensitive data.

@return One of the system wide error codes.

@internal
@test
*/
TInt DTest::DoRequest(TInt aFunction, TRequestStatus* aStatus, TAny* a1, TAny* a2)
	{
	TInt r=KErrNone;
    TInt func = (TInt)a1;
	switch (aFunction)
		{
		// A request to power up the SDIO card & stack
        case RSdioCardCntrlIf::EReqPwrUp:
			{
			if(!iSocketP->CardIsPresent())
				{
				// An SDIO card is not present
				Kern::QueueRequestComplete(iClient, iPowerUpRequest, KErrNotReady);
				}
			else if(iSocketP->State() == EPBusOn)
				{
				// The card is already powered up
				Kern::QueueRequestComplete(iClient, iPowerUpRequest, KErrNone);
				}
			else
				{
				// Power up the card
				iSocketP->PowerUp();
				}
			break;
			}

		// A request to read generic data from the SDIO card 
        case RSdioCardCntrlIf::ERequestReadDirect:
            {
            TInt addr = (TInt)a1;

    		TUint8 val = 0;
			r = iCardP->CommonRegisterInterface()->Read8(addr, &val);
			if(r == KErrNone)
				{
		   		iReadDirectRequest->Data() = (TUint)val;
				}
				
			Kern::QueueRequestComplete(iClient, iReadDirectRequest, r);
			break;
            }

    	// A request to reset the CIS pointer 
        case RSdioCardCntrlIf::ERequestResetCis:
            {
	        if ((r=CheckForChangeOfCis(func))==KErrNone)
	            {
			    iCisRd.Restart();
	            }
	            
			Kern::QueueRequestComplete(iClient, iResetCisRequest, r);
			break;
            }

       	// A request to read the card common config  
        case RSdioCardCntrlIf::ERequestGetCommonConfig:
            {
	        if ((r=CheckForChangeOfCis(func))==KErrNone)
                {
				memset(&iCardCommonReadRequest->Data(), 0, sizeof(TSDIOCardConfig));
				
		        r = iCisRd.FindReadCommonConfig(iCardCommonReadRequest->Data());
				Kern::QueueRequestComplete(iClient, iCardCommonReadRequest, r);
                }
			break;
            }

        // A request to read the function data (FBR)  
        case RSdioCardCntrlIf::ERequestGetFunctionConfig:
            {		        
	        if ((r=CheckForChangeOfCis(func))==KErrNone)
                {
				memset(&iFunctionCapsRequest->Data(), 0, sizeof(TSDIOFunctionCaps));
				
		        r=iCisRd.FindReadFunctionConfig(iFunctionCapsRequest->Data());	
				Kern::QueueRequestComplete(iClient, iFunctionCapsRequest, r);
                }            
			break;
            }

		default:
			r=KErrNotSupported;
			break;
		}
	return r;
	}

/**
Surround the DoControl command, creating a kernel copy of the user side data, then copying back afterwards

@param aMsg The message 
@return One of the system wide error codes.

@internal
@test
*/
TInt DTest::SendControl(TMessageBase* aMsg)
	{
	TThreadMessage& m = *(TThreadMessage*)aMsg;
    TInt id = m.iValue;

    TSdioCardInfo kernelCardInfo;
    TAny* userCardInfoPtr = m.Ptr0();
    
	// thread-local copy of configuration data	
	switch (id)
		{
		case RSdioCardCntrlIf::ESvCardInfo:
			// copy config from client to local buffer in context of client thread
			umemget32(&kernelCardInfo, userCardInfoPtr, sizeof(TSdioCardInfo));
			// update message to point to kernel-side buffer
			m.iArg[0] = &kernelCardInfo;
			break;
		}

	TInt r = DLogicalChannel::SendMsg(aMsg);
	if (r != KErrNone)
		return r;

	switch (id)
		{
		case RSdioCardCntrlIf::ESvCardInfo:
			// copy config from local bufferto client in context of client thread
			umemput32(userCardInfoPtr, &kernelCardInfo, sizeof(TSdioCardInfo));
			break;
		}

	return r;
	}

/**
Process any synchronous requests from the user side.

@param aFunction The synchronous function to invoke. 
@param a1 Context sensitive data.
@param a2 Context sensitive data.

@return One of the system wide error codes.

@internal
@test
*/
TInt DTest::DoControl(TInt aFunction, TAny* a1, TAny* /*a2*/)
	{
	TInt r=KErrNone;
	switch (aFunction)
		{
		// Read the card information data
		case RSdioCardCntrlIf::ESvCardInfo:
            {
			if(iCardP)
				{
				iCardP->CheckCIS();
				TSdioCardInfo* cardInfoPtr = (TSdioCardInfo*)a1;
				TSdioCardInfo& info = *cardInfoPtr;

				//
				// Extract the card information
				//
				info.isComboCard=iCardP->IsComboCard();				
	            info.iIsReady=iCardP->IsPresent();
	            info.iIsLocked=iCardP->IsLocked();
	            info.iCardSpeed=iCardP->MaxTranSpeedInKilohertz();
				TCID* cid=(TCID*)&(iCardP->CID());
				TInt i;
				for (i=0;i<16;i++)
					info.iCID[i]=cid->At(i);
				const TCSD& csd = iCardP->CSD();
				for (i=0;i<16;i++)
					info.iCSD[i]=csd.At(i);
	            info.iRCA=TUint16(iCardP->RCA());
	            info.iMediaType=TMmcMediaType(iCardP->MediaType());

				//
				// Extract the function information
				//
				info.iFuncCount = iCardP->FunctionCount();

				TSDIOFunctionCaps functionCaps;
				TSDIOFunction* functionP = NULL;

				for(TUint8 func=0; func<=info.iFuncCount; func++)
					{
					functionP = iCardP->IoFunction((TUint8) (func));
					if(functionP)
						{
						functionCaps = functionP->Capabilities();
						info.iFunction[func].iType = (TSdioFunctionType)(functionCaps.iType);
						}
					}
				}
			else
			    {
                r = KErrGeneral;
			    }

			break;
            }
		default:
			r=KErrNotSupported;
			break;
		}
	return r;
	}


/**
Check if diferent function selected, select new CIS if necessary.

@param aFunc The SDIO function. 

@return One of the system wide error codes.

@internal
@test
*/
TInt DTest::CheckForChangeOfCis(TInt aFunc)
	{

	if (iFunc!=aFunc||iFunc==KInvalidFuncNum)
		{
		TInt err;
		if ((err=iCisRd.SelectCis(KSocketNumber,0,0,(TUint8) aFunc))==KErrNone)
			iFunc=aFunc;
		return(err);
		}
	return(KErrNone);
	}

/**
Cancel an asynchronous request

@param aMask Mask of requests to cancel
@return One of the system wide error codes.

@internal
@test
*/
TInt DTest::DoCancel(TUint /*aMask*/)
	{	
	if (iPowerUpRequest->IsReady())
		{
		Kern::QueueRequestComplete(iClient, iPowerUpRequest, KErrCancel);
		}
	
	if (iResetCisRequest->IsReady())
		{
		Kern::QueueRequestComplete(iClient, iPowerUpRequest, KErrCancel);
		}

	if (iCardCommonReadRequest->IsReady())
		{
		Kern::QueueRequestComplete(iClient, iPowerUpRequest, KErrCancel);
		}

	if (iFunctionCapsRequest->IsReady())
		{
		Kern::QueueRequestComplete(iClient, iPowerUpRequest, KErrCancel);
		}

	if (iReadDirectRequest->IsReady())
		{
		Kern::QueueRequestComplete(iClient, iPowerUpRequest, KErrCancel);
		}
	
	return KErrNone;
	}

/**
Asynchronous call backs from the SDIO stack

@param aPtr Data passed in when the callback was registered.
@param aReason The reason for the callback, one of TPBusCallBack::EPBusStateChange
               or TPBusCallBack::EPBusCustomNotification.                  
@param a1 Context sensitive data.
@param a2 Context sensitive data.

@return One of the system wide error codes.

@internal
@test
*/
void DTest::EventCallBack(TAny* aPtr, TInt aReason, TAny* a1, TAny* a2)
	{
	DTest &mci = *(DTest*)aPtr;

	if (mci.iPowerUpRequest->IsReady())
		{
		// There is an TRequestStatus pending
		TInt retCode = KErrCompletion;

		switch(aReason)
			{
			// There has been a state change
			case TPBusCallBack::EPBusStateChange:
				{
				TPBusState newState = (TPBusState)(TInt)a1;
				TInt errorCode = (TInt)a2;

				switch(newState)
					{
					case EPBusCardAbsent:	retCode = KErrNotFound;		break;			
					case EPBusOff:			retCode = errorCode;		break;
					case EPBusPsuFault:		retCode = KErrBadPower;		break;
					case EPBusOn:			retCode = KErrNone;			break;
					case EPBusPowerUpPending:
					case EPBusPoweringUp:
					default:	
						break;
					}

				break;
				}
			}

		if(retCode != KErrCompletion)
			{
   			Kern::QueueRequestComplete(mci.iClient, mci.iPowerUpRequest, retCode);
			}
		}
	}