kerneltest/e32test/examples/convert1/convert1_ldd.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/examples/convert1/convert1_ldd.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,982 @@
+// Copyright (c) 2005-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:
+// its implementation.
+// 
+//
+
+/**
+ @file An example data converter device driver which uses Shared Chunks in
+ @publishedPartner
+ @prototype 9.1
+*/
+
+#include <kernel/kern_priv.h>
+#include <kernel/cache.h>
+#include "convert1.h"
+#include "convert1_dev.h"
+
+
+#if 0  // Set true for tracing
+#define TRACE(x) x
+#else
+#define TRACE(x)
+#endif
+
+
+_LIT(KConvert1PanicCategory,"CONVERT1");
+
+
+//
+// DConvert1Factory
+//
+
+/**
+  Number of hardware 'resources' available to driver.
+  E.g. the number of simultaneous channels it can support.
+*/
+const TInt KTotalConvert1Resources = 4;
+
+/**
+  A resource ID representing no resources
+*/
+const TInt KNullConvert1ResourceId = -1;
+
+/**
+  Standard export function for LDDs. This creates a DLogicalDevice derived object,
+  in this case, our DConvert1Factory
+*/
+DECLARE_STANDARD_LDD()
+	{
+	return new DConvert1Factory;
+	}
+
+/**
+  Constructor
+*/
+DConvert1Factory::DConvert1Factory()
+	{
+	// Set version number for this device
+	iVersion=RConvert1::VersionRequired();
+	// Indicate that do support units or a PDD
+	iParseMask=0;
+	// Mark all resources available
+	iResourceFlags = (1<<KTotalConvert1Resources)-1;
+	}
+
+/**
+  Second stage constructor for DConvert1Factory.
+  This must at least set a name for the driver object.
+
+  @return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DConvert1Factory::Install()
+	{
+	return SetName(&RConvert1::Name());
+	}
+
+/**
+  Destructor
+*/
+DConvert1Factory::~DConvert1Factory()
+	{
+	}
+
+/**
+  Return the drivers capabilities.
+  Called in the response to an RDevice::GetCaps() request.
+
+  @param aDes User-side descriptor to write capabilities information into
+*/
+void DConvert1Factory::GetCaps(TDes8& aDes) const
+	{
+	// Create a capabilities object
+	RConvert1::TCaps caps;
+	caps.iVersion = iVersion;
+	caps.iMaxChannels = KTotalConvert1Resources;
+
+	// Write it back to user memory
+	Kern::InfoCopy(aDes,(TUint8*)&caps,sizeof(caps));
+	}
+
+/**
+  Called by the kernel's device driver framework to create a Logical Channel.
+  This is called in the context of the user thread (client) which requested the creation of a Logical Channel
+  (E.g. through a call to RBusLogicalChannel::DoCreate)
+  The thread is in a critical section.
+
+  @param aChannel Set to point to the created Logical Channel
+
+  @return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DConvert1Factory::Create(DLogicalChannelBase*& aChannel)
+	{
+	aChannel=new DConvert1Channel(this);
+	if(!aChannel)
+		return KErrNoMemory;
+
+	return KErrNone;
+	}
+
+/**
+  Claim a hardware resource. This example driver has KTotalConvert1Resources
+  'hardware resources' and returns the ID of the next unallocated one.
+*/
+TInt DConvert1Factory::ClaimResource(TInt& aResourceId)
+	{
+	// Wait on mutex protecting resource allocation
+	NKern::FMWait(&iResourceMutex);
+
+	// Search for a free resource
+	TUint resourceFlags = iResourceFlags;
+	TUint mask = 1;
+	TInt id = 0;
+	do
+		{
+		if(resourceFlags&mask)
+			break;
+		mask <<= 1;
+		}
+	while(++id<KTotalConvert1Resources);
+
+	if(resourceFlags&mask)
+		iResourceFlags = resourceFlags&~mask; // Found resource, so mark it in use
+	else
+		id = KNullConvert1ResourceId; // No resource available
+
+	// Set returned resource id
+	aResourceId = id;
+
+	// Release mutex protecting resource allocation
+	NKern::FMSignal(&iResourceMutex);
+
+	return id<0 ? KErrInUse : KErrNone;
+	}
+
+/**
+  Released the hardware resource indicated by the given id
+*/
+void DConvert1Factory::ReleaseResource(TInt aResourceId)
+	{
+	// Do nothing if the null id was given
+	if(aResourceId==KNullConvert1ResourceId)
+		return;
+
+	// Wait on mutex protecting resource allocation
+	NKern::FMWait(&iResourceMutex);
+
+	// Check for valid resource and that it is not already free
+	__NK_ASSERT_DEBUG(TUint(aResourceId)<TUint(KTotalConvert1Resources));
+	__NK_ASSERT_DEBUG((iResourceFlags&(1<<aResourceId))==0);
+
+	// Flag resource free again
+	iResourceFlags |= 1<<aResourceId;
+
+	// Release mutex protecting resource allocation
+	NKern::FMSignal(&iResourceMutex);
+	}
+
+//
+// Logical Channel
+//
+
+/**
+  Default configuration (4k buffer, No Input Chunk, 1MB/sec speed)
+*/
+static const RConvert1::TConfig DefaultConfig = {4<<10,EFalse,1<<20};
+
+/**
+  Constructor
+*/
+DConvert1Channel::DConvert1Channel(DConvert1Factory* aFactory)
+	:	iFactory(aFactory), 
+		iResourceId(KNullConvert1ResourceId),
+		iConfig(DefaultConfig),
+		iConvertTimer(ConvertDfcTrampoline,this)
+	{
+	}
+
+/**
+  Second stage constructor called by the kernel's device driver framework.
+  This is called in the context of the user thread (client) which requested the creation of a Logical Channel
+  (E.g. through a call to RBusLogicalChannel::DoCreate)
+  The thread is in a critical section.
+
+  @param aUnit The unit argument supplied by the client to RBusLogicalChannel::DoCreate
+  @param aInfo The info argument supplied by the client to RBusLogicalChannel::DoCreate
+  @param aVer The version argument supplied by the client to RBusLogicalChannel::DoCreate
+
+  @return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DConvert1Channel::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& aVer)
+	{
+	// Check client has EMultimediaDD capability
+	if(!Kern::CurrentThreadHasCapability(ECapabilityMultimediaDD,__PLATSEC_DIAGNOSTIC_STRING("Checked by CAPTURE1")))
+		return KErrPermissionDenied;
+
+	// Check version
+	if (!Kern::QueryVersionSupported(RConvert1::VersionRequired(),aVer))
+		return KErrNotSupported;
+
+	// Claim ownership of a hardware resource
+	TInt r=iFactory->ClaimResource(iResourceId);
+	if(r!=KErrNone)
+		return r;
+
+	// Set client thread with which channel will be used by
+	iClient = &Kern::CurrentThread();
+
+	// Done
+	return KErrNone;
+	}
+
+/**
+  Destructor
+*/
+DConvert1Channel::~DConvert1Channel()
+	{
+	// Cancel outsatnding requests
+	DoCancel(RConvert1::EAllRequests);
+
+	// Release hardware resource which we own
+	iFactory->ReleaseResource(iResourceId);
+	}
+
+/**
+  Called when a user thread requests a handle to this channel.
+*/
+TInt DConvert1Channel::RequestUserHandle(DThread* aThread, TOwnerType aType)
+	{
+	// Make sure that only our client can get a handle
+	if (aType!=EOwnerThread || aThread!=iClient)
+		return KErrAccessDenied;
+	return KErrNone;
+	}
+
+/**
+  Process a request on this logical channel.
+
+  @param aReqNo Request number:
+  	            ==KMaxTInt, a 'DoCancel' message
+	            >=0, a 'DoControl' message with function number equal to iValue
+	            <0, a 'DoRequest' message with function number equal to ~iValue
+  @param a1     First argument. For DoRequest requests this is a pointer to the TRequestStatus.
+  @param a2     Second argument. For DoRequest this is a pointer to the 2 actual TAny* arguments.
+
+  @return       Result. Ignored by device driver framework for DoRequest requests.
+*/
+TInt DConvert1Channel::Request(TInt aReqNo, TAny* a1, TAny* a2)
+	{
+	// Decode the message type and dispatch it to the relevent handler function...
+	if ((TUint)aReqNo<(TUint)KMaxTInt)
+		return DoControl(aReqNo,a1,a2);
+	if(aReqNo==KMaxTInt)
+		return DoCancel((TInt)a1);
+	return DoRequest(aReqNo,a1,a2);
+	}
+
+/**
+  Process synchronous 'control' requests
+*/
+TInt DConvert1Channel::DoControl(TInt aFunction, TAny* a1, TAny* a2)
+	{
+	TRACE(Kern::Printf(">DConvert1Channel::DoControl fn=%d\n",aFunction);)
+
+	TInt r = KErrNotSupported;
+	switch (aFunction)
+		{
+		case RConvert1::EGetConfig:
+			r = GetConfig((TDes8*)a1);
+			break;
+
+		case RConvert1::ESetConfig:
+			r = SetConfig((const TDesC8*)a1,(RConvert1::TBufferInfo*)a2);
+			break;
+
+		case RConvert1::EConvertDes:
+			ConvertDes((const TDesC8*)a1,(TRequestStatus*)a2);
+			break;
+
+		case RConvert1::EConvertChunk:
+			ConvertChunk((const RConvert1::TConvertArgs*)a1,(TRequestStatus*)a2);
+			break;
+
+		case RConvert1::EConvertInChunk:
+			ConvertInChunk((TInt)a1,(TRequestStatus*)a2);
+			break;
+		}
+
+	TRACE(Kern::Printf("<DConvert1Channel::DoControl result=%d\n",r);)
+
+	return r;
+	}
+
+/**
+  Process asynchronous requests.
+  This driver doesn't have any 'DoRequest' requests because we handle asyncronous
+  requests using 'DoControl' for performance reasons. I.e. to avoid having to read
+  the arguments with kumemget()
+*/
+TInt DConvert1Channel::DoRequest(TInt aNotReqNo, TAny* a1, TAny* a2)
+	{
+	TRACE(Kern::Printf(">DConvert1Channel::DoRequest req=%d\n",aNotReqNo);)
+
+	// Get arguments
+	TAny* a[2];
+	kumemget32(a,a2,sizeof(a)); 
+	TRequestStatus* status=(TRequestStatus*)a1;
+	TInt reqNo = ~aNotReqNo;
+
+	// Do the request
+	TInt r;
+	switch(reqNo)
+		{
+		case RConvert1::EConvertDes:
+		case RConvert1::EConvertChunk:
+		case RConvert1::EConvertInChunk:
+			// Not used because we do these asyncronous request as a
+			// DoControl rather than a DoRequest for performance reasons.
+
+		default:
+			r = KErrNotSupported;
+			break;
+		}
+
+	// Complete request if there was an error
+	if (r!=KErrNone)
+		Kern::RequestComplete(&Kern::CurrentThread(),status,r);
+
+	TRACE(Kern::Printf("<DConvert1Channel::DoRequest result=%d\n",r);)
+
+	return KErrNone;  // Result is ignored by device driver framework for DoRequest requests
+	}
+
+/**
+  Process cancelling of asynchronous requests.
+*/
+TInt DConvert1Channel::DoCancel(TUint aMask)
+	{
+	TRACE(Kern::Printf(">DConvert1Channel::DoCancel mask=%08x\n",aMask);)
+
+	if(aMask&( (1<<RConvert1::EConvertDes) | (1<<RConvert1::EConvertChunk) | (1<<RConvert1::EConvertInChunk) ) )
+		ConvertCancel();
+
+	TRACE(Kern::Printf("<DConvert1Channel::DoCancel\n");)
+
+	return KErrNone;
+	}
+
+//
+// Methods for processing configuration control messages
+//
+
+/**
+  Process a GetConfig control message. This writes the current driver configuration to a
+  RConvert1::TConfigBuf supplied by the client.
+*/
+TInt DConvert1Channel::GetConfig(TDes8* aConfigBuf)
+	{
+	// Write the config to the client
+	Kern::InfoCopy(*aConfigBuf,(const TUint8*)&iConfig,sizeof(iConfig));
+	return KErrNone;
+	}
+
+/**
+  Process a SetConfig control message. This sets the driver configuration using a
+  RConvert1::TConfigBuf supplied by the client.
+*/
+TInt DConvert1Channel::SetConfig(const TDesC8* aConfigBuf,RConvert1::TBufferInfo* aBufferInfo)
+	{
+	// Create a config structure.
+	RConvert1::TConfig config(DefaultConfig);
+
+	// Note: We have constructed a config using DefaultConfig, this is to allow
+	// backwards compatibility when a client gives us an old (and shorter) version
+	// of the config structure.
+
+	// Read the config structure from client
+	TPtr8 ptr((TUint8*)&config,sizeof(config));
+	Kern::KUDesGet(ptr,*aConfigBuf);
+
+	// 'info' is the data we will return to client at the end
+	RConvert1::TBufferInfo info;
+	memclr(&info,sizeof(info));
+
+	TInt r;
+
+	// Need to be in critical section whilst allocating objects
+	NKern::ThreadEnterCS();
+
+	// Check we aren't in the middle of converting data
+	if(iConvertRequestStatus)
+		{
+		r = KErrInUse;
+		goto done;
+		}
+
+	// Note: The above check is enough to ensure we have exclusive access
+	// to this channels buffer and hardware resources because:
+	// 1. The covert DFC can't run because we haven't started converting yet.
+	// 2. No other request can come in because the channel only allows one
+	//    client thread to use it. See DConvert1Channel::Request()
+	// 3. The channel destructor can't be called whilst we are processing a request.
+
+
+	// For some settings we allow zero to mean default...
+	if(!config.iBufferSize)
+		config.iBufferSize = DefaultConfig.iBufferSize;
+	if(!config.iSpeed)
+		config.iSpeed = DefaultConfig.iSpeed;
+
+	// Validate configuration
+	if(config.iBufferSize<=0)
+		{
+		r = KErrArgument;
+		goto done;
+		}
+	if(config.iSpeed<=0)
+		{
+		r = KErrArgument;
+		goto done;
+		}
+
+	// Change the config
+	iConfig = config; 
+
+	{
+
+	// Calculate buffer size
+	TInt bufferSize = Kern::RoundToPageSize(config.iBufferSize);
+
+	// Destroy old buffers
+	iClientBuffer.Destroy();
+	iInBuffer.Destroy();
+	iOutBuffer.Destroy();
+
+	// Setup iClientBuffer
+	r = iClientBuffer.SetMaxSize(bufferSize);
+	if(r!=KErrNone)
+		goto done;
+
+	// Create output buffer
+	r = iOutBuffer.Create(bufferSize);
+	if(r!=KErrNone)
+		goto done;
+	// Make handle for output buffer
+	r = Kern::MakeHandleAndOpen(NULL, iOutBuffer.iChunk);
+	if(r<0) // -ve value is error, +ve value is a handle
+		goto done;
+	info.iOutChunkHandle = r;
+	r = KErrNone;
+
+	// Create input buffer if requested
+	if(iConfig.iCreateInputChunk)
+		{
+		r = iInBuffer.Create(bufferSize);
+		if(r!=KErrNone)
+			goto done;
+		// Make handle for input buffer
+		r = Kern::MakeHandleAndOpen(NULL, iInBuffer.iChunk);
+		if(r<0) // -ve value is error, +ve value is a handle
+			goto done;
+		info.iInChunkHandle = r;
+		r = KErrNone;
+		// Set info about input buffer
+		//
+		// Note we don't set iInBufferPtr because this is the address in
+		// client process which it must set for itself
+		info.iInBufferOffset = iInBuffer.iChunkOffset;
+		info.iInBufferSize = iInBuffer.iMaxSize;
+		}
+	}
+done:
+	// Cleanup if there was an error
+	if(r!=KErrNone)
+		{
+		iClientBuffer.Destroy();
+		iInBuffer.Destroy();
+		iOutBuffer.Destroy();
+		if(info.iOutChunkHandle)
+			Kern::CloseHandle(NULL,info.iOutChunkHandle);
+		if(info.iInChunkHandle)
+			Kern::CloseHandle(NULL,info.iInChunkHandle);
+		memclr(&info,sizeof(info));
+		}
+
+	NKern::ThreadLeaveCS();
+
+	// Write chunk handles and other info back to client memory
+	kumemput32(aBufferInfo,&info,sizeof(info));
+
+	return r;
+	}
+
+//
+// Methods for processing Convert requests
+//
+
+/**
+  Process Convert request where the source data is specified by a descriptor
+*/
+void DConvert1Channel::ConvertDes(const TDesC8* aSrc,TRequestStatus* aRequestStatus)
+	{
+	TInt r;
+
+	// Get descriptor info
+	TInt len;
+	TInt maxLen;
+	TAny* uptr = (TAny*)Kern::KUDesInfo(*aSrc,len,maxLen);
+
+	// Check there isn't an outstanding request
+	if(iConvertRequestStatus)
+		Kern::ThreadKill(NULL,EExitPanic,ERequestAlreadyPending,KConvert1PanicCategory);
+
+	// Check output buffer has been created
+	if(!iOutBuffer.iChunk)
+		{
+		r = KErrNotReady;
+		goto done;
+		}
+
+	// Check chunk has been created (TConfig::iCreateInputChunk True when SetConfig was called)
+	if(!iInBuffer.iChunk)
+		{
+		r = KErrNotSupported;
+		goto done;
+		}
+
+	// See if client data is in a shared chunk
+	r = iClientBuffer.Open(uptr,len);
+	if(r==KErrNone)
+		iSource = &iClientBuffer; // use iClientBuffer as input buffer
+	else
+		{
+		// Copy data from client descriptor into our iInBuffer
+		r = iInBuffer.Copy(uptr,len);
+		if(r==KErrNone)
+			iSource = &iInBuffer; // use iInBuffer as input buffer
+		}
+
+	// Start convert if no error
+	if(r==KErrNone)
+		{
+		iConvertRequestStatus = aRequestStatus;
+		DoConvertStart(0,len);
+		}
+done:
+	// Complete request if there was an error
+	if (r!=KErrNone)
+		Kern::RequestComplete(&Kern::CurrentThread(),aRequestStatus,r);
+	}
+
+/**
+  Process Convert request where the source data is specified by a chunk
+*/
+void DConvert1Channel::ConvertChunk(const RConvert1::TConvertArgs* aSrcArgs,TRequestStatus* aRequestStatus)
+	{
+	TInt r;
+
+	// Check there isn't an outstanding request
+	if(iConvertRequestStatus)
+		Kern::ThreadKill(NULL,EExitPanic,ERequestAlreadyPending,KConvert1PanicCategory);
+
+	// Check output buffer has been created
+	if(!iOutBuffer.iChunk)
+		{
+		r = KErrNotReady;
+		goto done;
+		}
+
+	// Unpackage arguments
+	RConvert1::TConvertArgs args;
+	kumemget32(&args,aSrcArgs,sizeof(args));
+
+	// Make buffer by opening chunk
+	r=iClientBuffer.Open(args.iChunkHandle,args.iOffset,args.iSize);
+
+	// Start convert if no error
+	if(r==KErrNone)
+		{
+		iSource = &iClientBuffer;
+		iConvertRequestStatus = aRequestStatus;
+		DoConvertStart(0,args.iSize);
+		}
+done:
+	// Complete request if there was an error
+	if (r!=KErrNone)
+		Kern::RequestComplete(&Kern::CurrentThread(),aRequestStatus,r);
+	}
+
+/**
+  Process Convert request where the source data is contained in the input chunk
+*/
+void DConvert1Channel::ConvertInChunk(TInt aSize,TRequestStatus* aRequestStatus)
+	{
+	TInt r;
+
+	// Check there isn't an outstanding request
+	if(iConvertRequestStatus)
+		Kern::ThreadKill(NULL,EExitPanic,ERequestAlreadyPending,KConvert1PanicCategory);
+
+	// Check output buffer has been created
+	if(!iOutBuffer.iChunk)
+		{
+		r = KErrNotReady;
+		goto done;
+		}
+
+	// Check chunk has been created (TConfig::iCreateInputChunk True when SetConfig was called)
+	if(!iInBuffer.iChunk)
+		{
+		r = KErrNotSupported;
+		goto done;
+		}
+
+	// Check size of data really fits within chunk
+	if(TUint(aSize)>=TUint(iInBuffer.iMaxSize))
+		{
+		r = KErrArgument;
+		goto done;
+		}
+
+	// Start the convert 
+	iSource = &iInBuffer;
+	iConvertRequestStatus = aRequestStatus;
+	DoConvertStart(iInBuffer.iChunkOffset,aSize);
+	r = KErrNone;
+
+done:
+	// Complete request if there was an error
+	if (r!=KErrNone)
+		Kern::RequestComplete(&Kern::CurrentThread(),aRequestStatus,r);
+	}
+
+/**
+  Signal ConvertData request completed
+*/
+void DConvert1Channel::ConvertCancel()
+	{
+	// Tell hardware to stop
+	DoConvertCancel();
+
+	// Complete client request
+	NKern::ThreadEnterCS();
+	ConvertComplete(KErrCancel);
+	NKern::ThreadLeaveCS();
+	}
+
+/**
+  DFC callback called after data has been converted.
+*/
+void DConvert1Channel::ConvertDfcTrampoline(TAny* aSelf)
+	{
+	// Just call non-static method
+	((DConvert1Channel*)aSelf)->ConvertDfc();
+	}
+
+/**
+  DFC callback called after data has been converted
+*/
+void DConvert1Channel::ConvertDfc()
+	{
+	TRACE(Kern::Printf(">DConvert1Channel::ConvertDfc\n");)
+
+	// The result value will be the chunk offset of the data we've converted
+	TInt result = iOutBuffer.iChunkOffset;
+	ConvertComplete(result);
+
+	TRACE(Kern::Printf("<DConvert1Channel::ConvertDfc\n");)
+	}
+
+/**
+  Complete a Convert request
+  @pre In thread critical section or DFC thread
+*/
+void DConvert1Channel::ConvertComplete(TInt aResult)
+	{
+	// Hold mutex to avoid concurrency
+	NKern::FMWait(&iConvertMutex);
+
+	// Claim the client request
+	TRequestStatus* status = iConvertRequestStatus;
+	iConvertRequestStatus = NULL;
+
+	// Claim chunk handle if we need to close it
+	DChunk* chunk = NULL;
+	if(status && iSource==&iClientBuffer)
+		{
+		chunk = iClientBuffer.iChunk;
+		iClientBuffer.iChunk = NULL;
+		}
+
+	// Clear iSource to help show up bugs
+	iSource = NULL;
+
+	// Can release mutex now we own the pointers
+	NKern::FMSignal(&iConvertMutex);
+
+	// Must be in a critical section so we can't die whilst owning 'chunk' and 'status'
+	__ASSERT_CRITICAL;
+
+	// Close chunk if required
+	if(chunk)
+		Kern::ChunkClose(chunk);
+
+	// Complete the request
+	if(status)
+		Kern::RequestComplete(iClient,status,aResult);
+	}
+
+
+//
+// DChunkBuffer
+//
+
+/**
+  Constructor
+*/
+DChunkBuffer::DChunkBuffer()
+	: iChunk(NULL), iPhysicalPages(NULL)
+	{
+	}
+
+/**
+  Create chunk and commit memory for buffer
+*/
+TInt DChunkBuffer::Create(TInt aBufferSize)
+	{
+	// Initialise member data for the size of buffer we want
+	TInt r=SetMaxSize(aBufferSize);
+	if(r!=KErrNone)
+		return r;
+
+	// Create chunk
+	__NK_ASSERT_DEBUG(!iChunk);
+	TChunkCreateInfo info;
+	info.iType = TChunkCreateInfo::ESharedKernelMultiple;
+	info.iMaxSize = (TInt)aBufferSize;
+#ifndef __WINS__
+	info.iMapAttr = EMapAttrCachedMax;
+#else
+	info.iMapAttr = 0;
+#endif
+	info.iOwnsMemory = ETrue;
+	r = Kern::ChunkCreate(info,iChunk,iChunkBase,iChunkMapAttr);
+
+	if(r==KErrNone)
+		{
+		// Commit memory to chunk
+		iChunkOffset = 0;
+		r = Kern::ChunkCommit(iChunk,iChunkOffset,Kern::RoundToPageSize(iMaxSize));
+		if(r==KErrNone)
+			{
+			// Setup physical address info for memory in the buffer
+			r = SetPhysicalAddresses(iMaxSize);
+			}
+		}
+
+	if(r!=KErrNone)
+		Destroy(); // Cleanup
+
+	return r;
+	}
+
+/**
+  Free all resources
+*/
+void DChunkBuffer::Destroy()
+	{
+	delete [] iPhysicalPages;
+	iPhysicalPages = 0;
+	Close();
+	}
+
+/**
+  Destructor
+*/
+DChunkBuffer::~DChunkBuffer()
+	{
+	Destroy();
+	}
+
+/**
+  Set maximum size for buffer.
+  (Allocates heap resources for this max size.)
+*/
+TInt DChunkBuffer::SetMaxSize(TInt aMaxSize)
+	{
+	// Create array to hold address of physical pages
+	__NK_ASSERT_DEBUG(!iPhysicalPages);
+	iPhysicalPages = new TPhysAddr[Kern::RoundToPageSize(aMaxSize)/Kern::RoundToPageSize(1)+1];
+	if(!iPhysicalPages)
+		return KErrNoMemory;
+
+	iMaxSize = aMaxSize;
+	return KErrNone;
+	}
+
+/**
+  Open a shared chunk given an user address and siae
+*/
+TInt DChunkBuffer::Open(TAny* aAddress, TInt aSize, TBool aWrite)
+	{
+	TInt r;
+
+	// Check size
+	if(aSize>iMaxSize)
+		return KErrTooBig;
+
+	NKern::ThreadEnterCS();
+
+	// Attempt to open chunk
+	iChunk = Kern::OpenSharedChunk(NULL,aAddress,aWrite,iChunkOffset);
+	if(!iChunk)
+		r = KErrArgument;
+	else
+		{
+		// Get physical addresses
+		r = SetPhysicalAddresses(aSize);
+		if(r!=KErrNone)
+			Close();
+		}
+
+	NKern::ThreadLeaveCS();
+
+	return r;
+	}
+
+/**
+  Open a specified shared chunk
+*/
+TInt DChunkBuffer::Open(TInt aChunkHandle, TInt aOffset, TInt aSize, TBool aWrite)
+	{
+	TInt r;
+
+	// Check size
+	if(aSize>iMaxSize)
+		return KErrTooBig;
+	iChunkOffset = aOffset;
+
+	NKern::ThreadEnterCS();
+
+	// Attempt to open chunk
+	iChunk = Kern::OpenSharedChunk(NULL,aChunkHandle,aWrite);
+	if(!iChunk)
+		r = KErrArgument;
+	else
+		{
+		// Get physical addresses
+		r = SetPhysicalAddresses(aSize);
+		if(r!=KErrNone)
+			Close();
+		}
+
+	NKern::ThreadLeaveCS();
+
+	return r;
+	}
+
+/**
+  Close chunk
+*/
+void DChunkBuffer::Close()
+	{
+	__ASSERT_CRITICAL;
+	if(iChunk)
+		{
+		Kern::ChunkClose(iChunk);
+		iChunk = NULL;
+		}
+	}
+
+/**
+  Fill buffer by copying data from the given user address
+*/
+TInt DChunkBuffer::Copy(TAny* aAddress, TInt aSize)
+	{
+	// Check size
+	if(aSize>iMaxSize)
+		return KErrTooBig;
+
+	// Copy data
+	kumemget((TAny*)(iChunkBase+iChunkOffset),aAddress,aSize);
+
+	return KErrNone;
+	}
+
+/**
+  Setup physical address info for memory in the buffer
+*/
+TInt DChunkBuffer::SetPhysicalAddresses(TInt aSize)
+	{
+	// Assert that the iPhysicalPages array already allocated will be big enough
+	__NK_ASSERT_DEBUG(aSize<=iMaxSize);
+
+	// Get physical addresses
+	TLinAddr kaddr;
+	TInt r=Kern::ChunkPhysicalAddress(iChunk,iChunkOffset,aSize,kaddr,iChunkMapAttr,iPhysicalAddress,iPhysicalPages);
+	// r = 0 or 1 on success. (1 meaning the physical pages are not contiguous)
+	if(r>=0) 
+		{
+		iChunkBase = kaddr-iChunkOffset; // Calculate start of chunk in kernel process address space
+		r = KErrNone;
+		}
+	return r;
+	}
+
+//
+// Program converter hardware
+//
+
+/**
+  Initialise hardware to start converting data.
+  Input data is in iSource.
+  Output data to be placed in iOutBuffer.
+*/
+void DConvert1Channel::DoConvertStart(TInt aOffset,TInt aSize)
+	{
+	// For this example test...
+	
+	TRACE(Kern::Printf("DConvert1Channel::DoConvertStart\n");)
+
+	// 'Convert' data by xoring with 1
+	TUint8* src = (TUint8*)iSource->iChunkBase+iSource->iChunkOffset+aOffset;
+	TUint8* end = src+aSize;
+	TUint8* dst = (TUint8*)iOutBuffer.iChunkBase+iOutBuffer.iChunkOffset;
+	while(src<end)
+		*dst++ = TUint8(*src++^1);
+
+	// Start the timer
+	TInt ticks = TInt((TInt64)1000000*(TInt64)aSize/(TInt64)iConfig.iSpeed)
+					/NKern::TickPeriod();
+	if(ticks<1)
+		ticks = 1;
+#ifdef _DEBUG
+	TInt r=
+#endif
+		iConvertTimer.OneShot(ticks,ETrue);
+	__NK_ASSERT_DEBUG(r==KErrNone);
+	}
+
+/**
+  Tell hardware to stop converting.
+*/
+void DConvert1Channel::DoConvertCancel()
+	{
+	// For this example test...
+	
+	TRACE(Kern::Printf("DConvert1Channel::DoConvertCancel\n");)
+
+	// Cancel the timer
+	iConvertTimer.Cancel();
+	}