/*
* Copyright (c) 2007-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: 
* Kernel-side implementation of the KMS LDD.
*
*/


/**
 @file
*/


#include "kmsextrldd.h"
#include "kmsextrlddk.h"


static void DeleteInCriticalSection(HBuf8* aBuf);

// -------- factory object --------


DKmsExtrLddFactory::DKmsExtrLddFactory()
/**
	Sets iVersion and iParseMask in DLogicalDevice superclass,

	iVersion specifies the currently supported version.
	iParseMask specifies
		Units are not supported (KDeviceAllowUnit is not set;)
		No PDD is required (KDeviceAllocPhysicalDevice is not set;)
		No additional information is used (KDeviceAllowInfo is not set.)
 */
	{
	iVersion = KmsExtrLddImpl::Version();
	iParseMask = 0;
	}


void DKmsExtrLddFactory::GetCaps(TDes8& aCapsDes) const
/**
	Implement DLogicalDevice by populating the supplied buffer
	with information about this logical device.
	
	Currently no information is supported and this function
	resets the supplied buffer.

	@param	aCapsDes		Buffer to populate with information
							about this logical device.
 */
	{
	aCapsDes.Zero();
	}


TInt DKmsExtrLddFactory::Install()
/**
	Implement DLogicalDevice by setting the device's name.

	@return					Symbian OS error code generated by call to SetName.
	@see DObject::SetName
 */
	{
	return SetName(&RKmsExtrChannel::FactoryName());
	}


TInt DKmsExtrLddFactory::Create(DLogicalChannelBase*& aChannel)
/**
	Implement DLogicalDevice by allocating a new instance of
	DKmsExtrLddChannel.

	@param	aChannel		On success this points to a newly-allocated
							and initialized channel object which is owned
							by the caller.  Its value is undefined on failure.
	@return					Symbian OS error code.
 */
	{
	aChannel = new DKmsExtrLddChannel;
	if (aChannel == 0)
		return KErrNoMemory;

	return KErrNone;
	}


// -------- channel --------


DKmsExtrLddChannel::DKmsExtrLddChannel()
/**
	Empty constructor.
 */
	{
	// empty.
	}

TInt DKmsExtrLddChannel::DoCreate(TInt aUnit, const TDesC8* aInfo, const TVersion& aVer)
/**
	Override DLogicalChannelBase by initializing this channel.

	@param	aUnit			Device unit.  Not used (KDeviceAllowUnit flag is not set
							in iParseMask.)
	@param	aInfo			Additional information supplied by user-side process about how to
							open channel.  Not used (KDeviceAllowInfo is not set in iParseMask.)
	@param	aVer			Requested version number.  This function will fail with KErrNotSupported
							if the driver does not support at least this version.
	@return					Symbian OS error code.  KErrNone indicates success, any other code means
							this function has failed.
 */
	{
	(void) aUnit;
	(void) aInfo;

	if (! Kern::QueryVersionSupported(/* aCurrent */ KmsExtrLddImpl::Version(), /* aRequested */ aVer))
		return KErrNotSupported;
	return KErrNone;
	}

DKmsExtrLddChannel::~DKmsExtrLddChannel()
/**
	Empty destructor.
 */
	{
	// empty.
	}

TInt DKmsExtrLddChannel::Request(TInt aReqNo, TAny* a1, TAny* a2)
/**
	Implement DLogicalChannel by processing a message from user-side.

	This function is called in the context of the thread which is using
	the handle.

	@param	aReqNo			Request identifier, taken from KmsExtrLddImpl::TControl.
							(Only controls, and not requests, are supported.)
	@param	a1				First call-specific argument passed to DoControl.
	@param	a2				Second call-specific argument passed to DoControl.
	@return					Symbian OS error code.  This is used to complete the
							user-side TRequestStatus object.
 */
	{
	TInt r;	
	HBuf8* data;

	KmsExtrLddImpl::TControl controlId = static_cast<KmsExtrLddImpl::TControl>(aReqNo);

	switch (controlId)
		{
	case KmsExtrLddImpl::EGetKeySize:
		// Getting the key length by allocating a copy of the data is not
		// an efficient way of going about it but, because this is test code
		// and so not worth adding another API to HwKeyStore.
		
		r = GetKeyData(a1, data);
		if (r == KErrNone)
			{
			TInt length = data->Length();
			DeleteInCriticalSection(data);		// don't leak if kumemput throws an exception
			umemput(a2, &length, sizeof(length));
			}
		break;
	
	case KmsExtrLddImpl::EGetKeyData:
		r = GetKeyData(a1, data);
		if (r == KErrNone)
			{
			DThread* curThread = &Kern::CurrentThread();
			r = Kern::ThreadDesWrite(curThread, a2, *data, /* aOffset */ 0);
			DeleteInCriticalSection(data);
			}
		
		break;
	
	default:
		r = KErrNotSupported;
		}
	
	return r;
	}

TInt DKmsExtrLddChannel::GetKeyData(TAny* aKdaPckgU, HBuf8*& aData)
/**
	Helper function for Request reads a key handle, operation pair
	from user-side, and attempts to extract the key's data.
	
	@param	aKdaPckgU		User-side packaged TKeyDataArgs object.
	@param	aData			On success this is set to a descriptor on the
							supervisor heap which contains a copy of the
							key data.  Its value is undefined on failure.
							Ownership is transferred to the caller.
	@return					Symbian OS error code.  KErrNone indicates success,
							any other value indicates failure.
 */
	{
	// read TKeyDataArgs object from user side
	KmsExtrLddImpl::TKeyDataArgs kda;
	TPckg<KmsExtrLddImpl::TKeyDataArgs> kdaPckgK(kda);
	DThread* curThread = &Kern::CurrentThread();
	TInt r = Kern::ThreadDesRead(curThread, aKdaPckgU, kdaPckgK, /* aOffset */ 0);
	if (r != KErrNone)
		return r;
	
	// extract key data from hardware store
	r = HwKeyStore::ExtractKey(curThread->iOwningProcess, kda.iHandle, kda.iOperation, aData);
	return r;
	}

static void DeleteInCriticalSection(HBuf8* aBuf)
/**
	Delete the supplied object within a critical section.
	User threads must be in a critical section when they free
	objects.

	@param aBuf				Heap-based descriptor object to delete.
 */
	{
	NKern::ThreadEnterCS();
	delete aBuf;
	NKern::ThreadLeaveCS();
	}

// -------- exported entrypoint --------


DECLARE_STANDARD_LDD()
/**
	This standard exported function allocates a new instance of the
	channel factory object.

	@return					New instance of DKmsExtrLddFactory.  This object has been
							allocated on the heap, but has not yet been initialized.
							(The calling framework does this by calling Install and
							Create.)  NULL if could not allocate heap memory.
	@see DKmsExtrLddFactory
 */
	{
	return new DKmsExtrLddFactory;
	}

