kerneltest/e32test/examples/camera1/camera1_ldd.cpp
changeset 9 96e5fb8b040d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/examples/camera1/camera1_ldd.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,1137 @@
+// 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 camera device driver which uses Shared Chunks in
+ @publishedPartner
+ @prototype 9.1
+*/
+
+#include <kernel/kern_priv.h>
+#include <kernel/cache.h>
+#include "camera1.h"
+#include "camera1_dev.h"
+
+#if 0  // Set true for tracing
+#define TRACE(x) x
+#else
+#define TRACE(x)
+#endif
+
+//
+// DCamera1Factory
+//
+
+/**
+  Standard export function for LDDs. This creates a DLogicalDevice derived object,
+  in this case, our DCamera1Factory
+*/
+DECLARE_STANDARD_LDD()
+	{
+	return new DCamera1Factory;
+	}
+
+/**
+  Constructor
+*/
+DCamera1Factory::DCamera1Factory()
+	{
+	// Set version number for this device
+	iVersion=RCamera1::VersionRequired();
+	// Indicate that do support units or a PDD
+	iParseMask=0;
+	}
+
+/**
+  Second stage constructor for DCamera1Factory.
+  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 DCamera1Factory::Install()
+	{
+	return SetName(&RCamera1::Name());
+	}
+
+/**
+  Destructor
+*/
+DCamera1Factory::~DCamera1Factory()
+	{
+	}
+
+/**
+  Return the drivers capabilities.
+  Called in the response to an RDevice::GetCaps() request.
+
+  @param aDes User-side descriptor to write capabilities information into
+*/
+void DCamera1Factory::GetCaps(TDes8& aDes) const
+	{
+	// Create a capabilities object
+	RCamera1::TCaps caps;
+	caps.iVersion = iVersion;
+	// 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 DCamera1Factory::Create(DLogicalChannelBase*& aChannel)
+	{
+	aChannel=new DCamera1Channel;
+	if(!aChannel)
+		return KErrNoMemory;
+
+	return KErrNone;
+	}
+
+//
+// Logical Channel
+//
+
+/**
+  Default configuration for driver (640x480 pixels of 32bits captured at 15 frames/sec)
+*/
+static const RCamera1::TConfig DefaultConfig = {{640,480},4,15};
+
+/**
+  Constructor
+*/
+DCamera1Channel::DCamera1Channel()
+	:	iDfcQ(Kern::TimerDfcQ()),  // This test uses the timer DFC queue for DFCs
+		iStateChangeDfc(StateChangeDfcTrampoline,this,1),  // DFC is priority '1'
+		iConfig(DefaultConfig),
+		iCaptureTimer(CaptureDfcTrampoline,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 DCamera1Channel::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(RCamera1::VersionRequired(),aVer))
+		return KErrNotSupported;
+
+	// Setup DFCs
+	iStateChangeDfc.SetDfcQ(iDfcQ);
+
+	// Done
+	return Kern::MutexCreate(iStateChangeMutex,KNullDesC,KMutexOrdGeneral7);
+	}
+
+/**
+  Destructor
+*/
+DCamera1Channel::~DCamera1Channel()
+	{
+	DoCancel(RCamera1::EAllRequests);
+	EndCapture();
+	iStateChangeDfc.Cancel();
+	if(iStateChangeMutex)
+		iStateChangeMutex->Close(0);
+	if(iCaptureBuffers)
+		iCaptureBuffers->Close();
+	}
+
+/**
+  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 DCamera1Channel::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 DCamera1Channel::DoControl(TInt aFunction, TAny* a1, TAny* a2)
+	{
+	TRACE(Kern::Printf(">DCamera1Channel::DoControl fn=%d\n",aFunction);)
+
+	(void)a2;   // a2 not used in this example
+
+	TInt r = KErrNotSupported;
+	switch (aFunction)
+		{
+		case RCamera1::EGetConfig:
+			r = GetConfig((TDes8*)a1);
+			break;
+
+		case RCamera1::ESetConfig:
+			r = SetConfig((const TDesC8*)a1);
+			break;
+
+		case RCamera1::EStartCapture:
+			r = StartCapture();
+			break;
+
+		case RCamera1::EEndCapture:
+			r = EndCapture();
+			break;
+
+		case RCamera1::EReleaseImage:
+			r = ImageRelease((TInt)a1);
+			break;
+
+		case RCamera1::ECaptureImage:
+			CaptureImage((TRequestStatus*)a1,(TInt)a2);
+			break;
+		}
+
+	TRACE(Kern::Printf("<DCamera1Channel::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 DCamera1Channel::DoRequest(TInt aNotReqNo, TAny* a1, TAny* a2)
+	{
+	TRACE(Kern::Printf(">DCamera1Channel::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 RCamera1::ECaptureImage:
+			// Not used because we do 'ECaptureImage' 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("<DCamera1Channel::DoRequest result=%d\n",r);)
+
+	return KErrNone;  // Result is ignored by device driver framework for DoRequest requests
+	}
+
+/**
+  Process cancelling of asynchronous requests.
+*/
+TInt DCamera1Channel::DoCancel(TUint aMask)
+	{
+	TRACE(Kern::Printf(">DCamera1Channel::DoCancel mask=%08x\n",aMask);)
+
+	if(aMask&(1<<RCamera1::ECaptureImage))
+		CaptureImageCancel();
+
+	TRACE(Kern::Printf("<DCamera1Channel::DoCancel\n");)
+
+	return KErrNone;
+	}
+
+//
+// Methods for processing configuration control messages
+//
+
+/**
+  Process a GetConfig control message. This writes the current driver configuration to a
+  RCamera1::TConfigBuf supplied by the client.
+*/
+TInt DCamera1Channel::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
+  RCamera1::TConfigBuf supplied by the client.
+*/
+TInt DCamera1Channel::SetConfig(const TDesC8* aConfigBuf)
+	{
+	// Create a config structure.
+	RCamera1::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);
+
+	// For some settings we allow zero to mean default...
+	if(!config.iImageSize.iWidth)
+		config.iImageSize.iWidth = DefaultConfig.iImageSize.iWidth;
+	if(!config.iImageSize.iHeight)
+		config.iImageSize.iHeight = DefaultConfig.iImageSize.iHeight;
+	if(!config.iImageBytesPerPixel)
+		config.iImageBytesPerPixel = DefaultConfig.iImageBytesPerPixel;
+
+	// Validate configuration
+	TInt scale = DefaultConfig.iImageSize.iWidth/config.iImageSize.iWidth;
+	if(scale*config.iImageSize.iWidth != DefaultConfig.iImageSize.iWidth)
+		return KErrArgument;
+	if(scale*config.iImageSize.iHeight != DefaultConfig.iImageSize.iHeight)
+		return KErrArgument;
+	if(config.iImageBytesPerPixel<=0 || config.iImageBytesPerPixel>4)
+		return KErrArgument;
+
+	if(config.iFrameRate<0)
+		return KErrArgument;
+	if(config.iNumImageBuffers<1)
+		return KErrArgument;
+
+	TInt imageSize;
+	DCaptureBuffers* buffers;
+	TInt r;
+
+	// Need to be in critical section whilst holding a DMutex
+	NKern::ThreadEnterCS();
+
+	// Claim state change mutex. Note, the return value is ignored because a Wait
+	// can only fail if the mutex is destroyed whilst waiting for it, this can't 
+	// happen in our driver.
+	Kern::MutexWait(*iStateChangeMutex);
+
+	// Check we aren't in the middle of capturing images
+	if(iCapturing)
+		{
+		r = KErrInUse;
+		goto done;
+		}
+
+	// Change the config
+	iConfig = config; 
+	iCaptureRateTicks = config.iFrameRate ? 1000000/config.iFrameRate/NKern::TickPeriod() : KMaxTInt;
+	if(iCaptureRateTicks<1)
+		iCaptureRateTicks = 1;
+
+	// Claim ownership of old buffers
+	NKern::FMWait(&iCaptureMutex);
+	buffers = iCaptureBuffers;
+	iCaptureBuffers = NULL;
+	NKern::FMSignal(&iCaptureMutex);
+
+	// Delete old buffers
+	if(buffers)
+		buffers->Close();
+
+	// Contruct new buffer object
+	imageSize = iConfig.iImageSize.iWidth*iConfig.iImageSize.iHeight*iConfig.iImageBytesPerPixel;
+	buffers = DCaptureBuffers::New(2+iConfig.iNumImageBuffers,imageSize);
+	if(!buffers)
+		{
+		r = KErrNoMemory;
+		goto done;
+		}
+
+	// Use the new buffers if another thread didn't create them first
+	NKern::FMWait(&iCaptureMutex);
+	iCaptureBuffers = buffers;
+	NKern::FMSignal(&iCaptureMutex);
+
+	// Create handle for chunk
+	r = Kern::MakeHandleAndOpen(NULL, iCaptureBuffers->iChunk);
+
+done:
+	// Release state change mutex
+	Kern::MutexSignal(*iStateChangeMutex);
+
+	NKern::ThreadLeaveCS();
+
+	return r;
+	}
+
+//
+// Methods for processing start/end capture
+//
+
+/**
+   Start image capturing
+*/
+TInt DCamera1Channel::StartCapture()
+	{
+	// Need to be in critical section whilst holding a DMutex
+	NKern::ThreadEnterCS();
+
+	// Claim state change mutex. Note, the return value is ignored because a Wait
+	// can only fail if the mutex is destroyed whilst waiting for it, this can't 
+	// happen in our driver.
+	Kern::MutexWait(*iStateChangeMutex);
+
+	NKern::FMWait(&iCaptureMutex);
+
+	TInt r;
+	if(!iCaptureBuffers)
+		r = KErrNotReady;  // SetConfig not yet been called
+	else if(iCapturing)
+		r = KErrInUse;     // StartCapture has already been called
+	else
+		{
+		// Initialise image buffer state for capturing images
+		iCaptureBuffers->Reset();
+
+		// Flag capturing started
+		iCapturing = ETrue;
+		r = KErrNone;
+		}
+
+	NKern::FMSignal(&iCaptureMutex);
+
+	// Get state change DFC to initialise camera hardware for capture
+	if(r==KErrNone)
+		StateChange(ETrue);
+
+	// Release state change mutex
+	Kern::MutexSignal(*iStateChangeMutex);
+
+	NKern::ThreadLeaveCS();
+
+	return r;
+	}
+
+/**
+   End image capturing
+*/
+TInt DCamera1Channel::EndCapture()
+	{
+	// Need to be in critical section whilst holding a DMutex
+	NKern::ThreadEnterCS();
+
+	// Claim state change mutex. Note, the return value is ignored because a Wait
+	// can only fail if the mutex is destroyed whilst waiting for it, this can't 
+	// happen in our driver.
+	Kern::MutexWait(*iStateChangeMutex);
+
+	if(iCapturing)
+		{
+		// Get state change DFC to reset camera hardware
+		StateChange(EFalse);
+
+		// Flag capture ended
+		NKern::FMWait(&iCaptureMutex);
+		iCapturing = EFalse;
+		NKern::FMSignal(&iCaptureMutex);
+
+		// Cancel any pending caoture request
+		CaptureImageCancel();
+		}
+
+	// Release state change mutex
+	Kern::MutexSignal(*iStateChangeMutex);
+
+	NKern::ThreadLeaveCS();
+
+	return KErrNone;
+	}
+
+/**
+  Performs state change on Start/EndCapture by calling state change DFC
+  Call with iStateChangeMutex held.
+
+  @param aNewState True to start image capture, false to stop image capture.
+*/
+void DCamera1Channel::StateChange(TBool aNewState)
+	{
+	iNewState = aNewState;
+	NKern::FSSetOwner(&iStateChangeSemaphore,NULL);
+	iStateChangeDfc.Enque();
+	NKern::FSWait(&iStateChangeSemaphore);
+	}
+
+/**
+  DFC callback called when Start/EndCapture requests are made.
+*/
+void DCamera1Channel::StateChangeDfcTrampoline(TAny* aSelf)
+	{
+	// Just call non-static method
+	((DCamera1Channel*)aSelf)->StateChangeDfc();
+	}
+
+/**
+  DFC callback called when Start/EndCapture requests are made.
+*/
+void DCamera1Channel::StateChangeDfc()
+	{
+	TRACE(Kern::Printf(">DCamera1Channel::StateChangeDfc\n");)
+
+	// Call relevent state change function
+	if(iNewState)
+		DoStartCapture();
+	else
+		DoEndCapture();
+
+	// Signal completion
+	NKern::FSSignal(&iStateChangeSemaphore);
+
+	TRACE(Kern::Printf("<DCamera1Channel::StateChangeDfc\n");)
+	}
+
+//
+// Methods for processing CaptureImage
+//
+
+/**
+  Process Capture Image request 
+*/
+void DCamera1Channel::CaptureImage(TRequestStatus* aRequestStatus,TInt aReleaseImage)
+	{
+	TInt r=KErrNone;
+
+	// Get the thread making the request
+	DThread* requestThread = &Kern::CurrentThread();
+
+	// Release image (if one was specified)
+	if(aReleaseImage!=-1)
+		{
+		r = ImageRelease(aReleaseImage);
+		if(r!=KErrNone)
+			goto done;
+		}
+
+	NKern::FMWait(&iCaptureMutex);
+
+	if(!iCapturing)
+		r = KErrNotReady;     // StartCapture hasn't yet been called
+	else if(iCaptureRequestStatus)
+		r = KErrInUse;        // There is already a pending CaptureImage request
+	else
+		{
+		// See if an image is already available...
+		DImageBuffer* buffer=iCaptureBuffers->ImageForClient();
+		if(buffer)
+			{
+			// Return offset of buffer to client
+			r = buffer->iChunkOffset;
+			}
+		else
+			{
+			// Image not found...
+			if(!iCaptureBuffers->iFreeBuffers[0])
+				r = KErrOverflow;  // Out of buffers
+			else
+				{
+				// Wait for new image to become available
+				iCaptureRequestStatus = aRequestStatus;
+				requestThread->Open(); // can't fail because this is the current thread
+				iCaptureRequestThread = requestThread;
+				r = KErrNone;
+				}
+			}
+		}
+
+	NKern::FMSignal(&iCaptureMutex);
+
+done:
+	// Complete request if there was an error
+	if (r!=KErrNone)
+		Kern::RequestComplete(requestThread,aRequestStatus,r);
+	}
+
+/**
+  Signal Capture Image request completed
+*/
+void DCamera1Channel::CaptureImageCancel()
+	{
+	// Need to be in critical section so we don't die whilst owning the capture image request
+	NKern::ThreadEnterCS();
+
+	// Claim the capture image request
+	NKern::FMWait(&iCaptureMutex);
+	DThread* thread = iCaptureRequestThread;;
+	TRequestStatus* status = iCaptureRequestStatus;
+	iCaptureRequestStatus = NULL;
+	NKern::FMSignal(&iCaptureMutex);
+
+	// Signal completion
+	if(status)
+		{
+		Kern::RequestComplete(thread,status,KErrCancel);
+		thread->Close(0);
+		}
+
+	NKern::ThreadLeaveCS();
+	}
+
+/**
+  DFC callback called when after a new image has been captured
+  In this example code this is called by
+*/
+void DCamera1Channel::CaptureDfcTrampoline(TAny* aSelf)
+	{
+	// Just call non-static method
+	((DCamera1Channel*)aSelf)->CaptureDfc();
+	}
+
+/**
+  DFC callback called when a new image has been captured
+*/
+void DCamera1Channel::CaptureDfc()
+	{
+	TRACE(Kern::Printf(">DCamera1Channel::CaptureDfc\n");)
+
+	NKern::FMWait(&iCaptureMutex);
+
+	// Update image buffers state
+	iCaptureBuffers->ImageCaptured();
+
+	// Did client request an image and is one available?
+	DImageBuffer* clientBuffer;
+	if(iCaptureRequestStatus && (clientBuffer=iCaptureBuffers->ImageForClient())!=NULL )
+		{
+		// Claim the client request
+		DThread* thread = iCaptureRequestThread;
+		TRequestStatus* status = iCaptureRequestStatus;
+		iCaptureRequestStatus = NULL;
+
+		NKern::FMSignal(&iCaptureMutex);
+
+		// We now own the client request but we don't have to worry about
+		// being in a critical section because we are running in a DFC thread
+		// which can't be killed
+
+		// Complete client request with the chunk offset for a captured image
+		// (We use AsyncClose() here because we are running in a high priority DFC and
+		// don't want to take the penalty for possibly deleting a thread in this context.)
+		Kern::RequestComplete(thread,status,clientBuffer->iChunkOffset);
+		thread->AsyncClose();
+		}
+	else
+		NKern::FMSignal(&iCaptureMutex);
+
+	// Get camera hardware to capture next image
+	DoNextCapture();
+
+	TRACE(Kern::Printf("<DCamera1Channel::CaptureDfc\n");)
+	}
+
+/**
+  Release a buffer which was being used by client
+
+  @param aChunkOffset The chunk offset corresponding to the buffer to be freed
+
+  @return KErrNone if successful.
+		  KErrNotFound if no 'in use' buffer had the specified chunk offset.
+*/
+TInt DCamera1Channel::ImageRelease(TInt aChunkOffset)
+	{
+	// Need to be in critical section so we don't die whilst holding reference on buffers
+	NKern::ThreadEnterCS();
+
+	// Get reference to buffers object and find the buffer we want
+	NKern::FMWait(&iCaptureMutex);
+	DCaptureBuffers* buffers = iCaptureBuffers;
+	DImageBuffer* buffer = NULL;
+	if(buffers)
+		{
+		buffers->Open();
+		buffer = buffers->InUseImage(aChunkOffset);
+		}
+	NKern::FMSignal(&iCaptureMutex);
+
+	TInt r;
+	if(!buffer)
+		r = KErrNotFound;	// Buffer not found
+	else
+		{
+		// Purge the CPU cache for the buffer.
+		// Note, we don't do this whilst holding iCaptureMutex because it can
+		// take a long time.
+		// Also, it doesn't mater that e aren't holding the mutex because:
+		// 1. The buffer can't be delete because we have a reference count on iCaptureBuffers
+		// 2. Reentrancy of the Purge method is safe 
+		buffers->Purge(buffer);
+
+		// Release buffer (move it to the free list)
+		NKern::FMWait(&iCaptureMutex);
+		r = buffers->ImageRelease(aChunkOffset) ? KErrNone : KErrArgument;
+		NKern::FMSignal(&iCaptureMutex);
+		}
+
+	// Close reference on buffers
+	if(buffers)
+		buffers->Close();
+
+	NKern::ThreadLeaveCS();
+
+	return r;
+	}
+
+//
+// DCaptureBuffers
+//
+
+/**
+  Construct a new set of buffers
+
+  @param aNumBuffers Number of buffers
+  @param aBufferSize Size of each buffer in bytes
+
+  @return Pointer to the created DCaptureBuffers or NULL if the system ran out of memory
+*/
+DCaptureBuffers* DCaptureBuffers::New(TInt aNumBuffers,TInt aBufferSize)
+	{
+	DCaptureBuffers* buffers = new DCaptureBuffers;
+	if(buffers)
+		{
+		TInt r = buffers->Create(aNumBuffers,aBufferSize);
+		if(r==KErrNone)
+			return buffers;
+		delete buffers;
+		// An error other than 'no memory' must be a programming error in the driver
+		__NK_ASSERT_ALWAYS(r==KErrNoMemory);
+		}
+	return NULL;
+	}
+
+/**
+  Construct with access count of one
+*/
+DCaptureBuffers::DCaptureBuffers()
+	: iAccessCount(1)
+	{
+	}
+
+/**
+  Create all buffers and lists
+*/
+TInt DCaptureBuffers::Create(TInt aNumBuffers,TInt aBufferSize)
+	{
+	// Allocate buffer lists
+	DImageBuffer** lists = (DImageBuffer**)Kern::AllocZ(3*aNumBuffers*sizeof(DImageBuffer*));
+	if(!lists)
+		return KErrNoMemory;
+	iBufferLists = lists;
+	iFreeBuffers = lists;
+	iCompletedBuffers = lists+aNumBuffers;
+	iInUseBuffers = lists+2*aNumBuffers;
+
+	// Calculate sizes
+	aBufferSize = Kern::RoundToPageSize(aBufferSize);
+	TInt pageSize = Kern::RoundToPageSize(1);
+	TUint64 chunkSize = TUint64(aBufferSize+pageSize)*aNumBuffers+pageSize;
+	if(chunkSize>(TUint64)KMaxTInt)
+		return KErrNoMemory;  // Need more than 2GB of memory!
+
+	// Create chunk
+	TChunkCreateInfo info;
+	info.iType = TChunkCreateInfo::ESharedKernelMultiple;
+	info.iMaxSize = (TInt)chunkSize;
+#ifndef __WINS__
+	info.iMapAttr = EMapAttrCachedMax;
+#else
+	info.iMapAttr = 0;
+#endif
+	info.iOwnsMemory = ETrue;
+	TInt r = Kern::ChunkCreate(info,iChunk,iChunkBase,iChunkMapAttr);
+	if(r!=KErrNone)
+		return r;
+
+	// Construct array of buffers
+	iNumBuffers = aNumBuffers;
+	iImageBuffer = new DImageBuffer[aNumBuffers];
+	if(!iImageBuffer)
+		return KErrNoMemory;
+
+	// Create each buffer
+	TInt offset = pageSize;
+	while(aNumBuffers)
+		{
+		r = iImageBuffer[--aNumBuffers].Create(iChunk,offset,aBufferSize);
+		if(r!=KErrNone)
+			return r;
+		offset += aBufferSize+pageSize;
+		}
+
+	return KErrNone;
+	}
+
+/**
+  Destructor
+*/
+DCaptureBuffers::~DCaptureBuffers()
+	{
+	if(iChunk)
+		Kern::ChunkClose(iChunk);
+	delete [] iImageBuffer;
+	Kern::Free(iBufferLists);
+	}
+
+/**
+  Increment access count of buffers
+*/
+void DCaptureBuffers::Open()
+	{
+	__e32_atomic_tas_ord32(&iAccessCount, 1, 1, 0);
+	}
+
+/**
+  Decrement access count of buffers.
+  Deleting them if the count is decremented to zero.
+*/
+void DCaptureBuffers::Close()
+	{
+	__ASSERT_NO_FAST_MUTEX;
+	__ASSERT_CRITICAL;
+	if(__e32_atomic_tas_ord32(&iAccessCount, 1, -1, 0) == 1)
+		AsyncDelete();
+	}
+
+/**
+  Reset all image buffer lists to reflect the state at the start of image capture process
+*/
+void DCaptureBuffers::Reset()
+	{
+	// Purge cache for all buffers in use by client.
+	DImageBuffer** list = iInUseBuffers;
+	DImageBuffer* buffer;
+	while((buffer=*list++)!=NULL)
+		Purge(buffer);
+
+	// Get pointers to first buffer
+	buffer = iImageBuffer; 
+
+	// Set buffers for current and next images
+	iCurrentBuffer = buffer++;
+	iNextBuffer = buffer++;
+
+	// Add all other buffers to the free list
+	DImageBuffer** free = iFreeBuffers;
+	DImageBuffer* bufferLimit = iImageBuffer+iNumBuffers; 
+	while(buffer<bufferLimit)
+		*free++ = buffer++;
+	*free = 0;
+
+	// Start with no completed or used buffers
+	iCompletedBuffers[0] = 0;
+	iInUseBuffers[0] = 0;
+	}
+
+/**
+  Purge cache for an image buffer.
+  @param aBuffer The buffer.
+*/
+void DCaptureBuffers::Purge(DImageBuffer* aBuffer)
+	{
+	Cache::SyncMemoryBeforeDmaRead(iChunkBase+aBuffer->iChunkOffset,aBuffer->iSize,iChunkMapAttr);
+	}
+
+/**
+  Remove an image buffer to the start of the given image list.
+  @return A pointer to the image buffer or NULL if the list was empty
+*/
+DImageBuffer* DCaptureBuffers::Remove(DImageBuffer** aList)
+	{
+	DImageBuffer* buffer=aList[0];
+	if(buffer)
+		{
+		DImageBuffer* b;
+		do
+			{
+			b=aList[1];
+			*aList++ = b;
+			}
+		while(b);
+		}
+	return buffer;
+	}
+
+/**
+  Add an image buffer to the end of the given image list.
+*/
+DImageBuffer* DCaptureBuffers::Add(DImageBuffer** aList, DImageBuffer* aBuffer)
+	{
+	while(*aList) aList++;
+	*aList = aBuffer;
+	return aBuffer;
+	}
+
+/**
+  Update buffer lists after an image has been captured.
+  @return A pointer to the catptured image buffer
+*/
+DImageBuffer* DCaptureBuffers::ImageCaptured()
+	{
+	// Add captured image to completed list
+	DImageBuffer* buffer = iCurrentBuffer;
+	DCaptureBuffers::Add(iCompletedBuffers,buffer);
+
+	// Make queued buffer the current one
+	iCurrentBuffer = iNextBuffer;
+
+	// Queue a new buffer
+	iNextBuffer = DCaptureBuffers::Remove(iFreeBuffers);
+	if(!iNextBuffer)
+		iNextBuffer = DCaptureBuffers::Remove(iCompletedBuffers);
+
+	TRACE(Kern::Printf("DCaptureBuffers::ImageCaptured  buf=%08x\n",buffer->iChunkOffset);)
+
+	return buffer;
+	}
+
+/**
+  Get the next image from the completed capture list and make it 'in use' by the client
+
+  @return A pointer to the next completed image buffer
+*/
+DImageBuffer* DCaptureBuffers::ImageForClient()
+	{
+	DImageBuffer* buffer=Remove(iCompletedBuffers);
+	if(buffer)
+		DCaptureBuffers::Add(iInUseBuffers,buffer);
+
+	TRACE(Kern::Printf("DCaptureBuffers::ImageForClient buf=%08x\n",buffer ? buffer->iChunkOffset : -1);)
+
+	return buffer;
+	}
+
+/**
+  Release (move to free list) the 'in use' image specified by the given chunk offset.
+
+  @param aChunkOffset The chunk offset corresponding to the buffer to be freed
+
+  @return The freed image buffer, or NULL if no 'in use' buffer had the specified chunk offset.
+*/
+DImageBuffer* DCaptureBuffers::ImageRelease(TInt aChunkOffset)
+	{
+	// Scan 'in use' list for the image buffer
+	DImageBuffer** list = iInUseBuffers;
+	DImageBuffer* buffer;
+	while((buffer=*list++)!=NULL && buffer->iChunkOffset!=aChunkOffset)
+		{};
+
+	// Move buffer to the free list (if found)
+	if(buffer)
+		buffer = Add(iFreeBuffers,Remove(list-1));
+
+	TRACE(Kern::Printf("DCaptureBuffers::ImageRelease   buf=%08x\n",buffer ? buffer->iChunkOffset : -1);)
+
+	return buffer;
+	}
+
+/**
+  Find the 'in use' image specified by the given chunk offset
+
+  @param aChunkOffset The chunk offset corresponding to the buffer to be freed
+
+  @return The image buffer, or NULL if no 'in use' buffer had the specified chunk offset
+*/
+DImageBuffer* DCaptureBuffers::InUseImage(TInt aChunkOffset)
+	{
+	// Scan 'in use' list for the image buffer
+	DImageBuffer** list = iInUseBuffers;
+	DImageBuffer* buffer;
+	while((buffer=*list++)!=NULL && buffer->iChunkOffset!=aChunkOffset)
+		{};
+
+	return buffer;
+	}
+
+//
+// DImageBuffer
+//
+
+/**
+  Constructor clears all member data
+*/
+DImageBuffer::DImageBuffer()
+	{
+	memclr(this,sizeof(*this));
+	}
+
+/**
+  Commit memory for this buffer.
+
+  @param aChunk  The chunk into which the memory is to be commited
+  @param aOffset The offset within aChunk for the start of the comitted memory.
+                 Must be a multiple of the MMU page size.
+  @param aSize   The number of bytes of memory to commit.
+                 Must be a multiple of the MMU page size.
+
+  @return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DImageBuffer::Create(DChunk* aChunk, TInt aOffset, TInt aSize)
+	{
+	TInt r;
+
+	// Initialise data
+	iChunkOffset = aOffset;
+	iSize = aSize;
+
+	// Try for physically contiguous memory first
+	r = Kern::ChunkCommitContiguous(aChunk,aOffset,aSize,iPhysicalAddress);
+	if(r==KErrNone)
+		return r;
+
+	// failed to get contiguous memory...
+
+	// Mark physical address invalid
+	iPhysicalAddress = KPhysAddrInvalid;
+
+	// Commit discontiguous memory
+	r = Kern::ChunkCommit(aChunk,aOffset,aSize);
+	if(r!=KErrNone)
+		return r;
+
+	// Allocate array for list of physical pages
+	iPhysicalPages = new TPhysAddr[aSize/Kern::RoundToPageSize(1)];
+	if(!iPhysicalPages)
+		return KErrNoMemory;
+
+	// Get physical addresses of pages in buffer
+	TUint32 kernAddr;
+	TUint32 mapAttr;
+	TPhysAddr physAddr;
+	r = Kern::ChunkPhysicalAddress(aChunk,aOffset,aSize,kernAddr,mapAttr,physAddr,iPhysicalPages);
+	// r = 0 or 1 on success. (1 meaning the physical pages are not-contiguous)
+	if(r>=0)
+		r = KErrNone;
+	return r;
+	}
+
+/**
+  Destructor
+*/
+DImageBuffer::~DImageBuffer()
+	{
+	delete [] iPhysicalPages;
+	}
+
+//
+// Program camera hardware
+//
+
+/**
+  Initialise camera hardware to start capturing images
+  First buffer to fill is iCaptureBuffers->iCurrentBuffer.
+  Next buffer to fill will be iCaptureBuffers->iNextBuffer.
+*/
+void DCamera1Channel::DoStartCapture()
+	{
+	// For this example test...
+	
+	TRACE(Kern::Printf("DCamera1Channel::DoStartCapture buf=%08x cnt=%04d\n",iCaptureBuffers->iCurrentBuffer->iChunkOffset,iCaptureCounter);)
+
+	// Initialise frame counter
+	iCaptureCounter = 0;
+
+	// Put frame counter into current image buffer. (This is the 'image' data we capture).
+	*(TInt*)(iCaptureBuffers->iChunkBase+iCaptureBuffers->iCurrentBuffer->iChunkOffset) = iCaptureCounter++;
+
+	// Start the timer
+	TInt r=iCaptureTimer.OneShot(iCaptureRateTicks,ETrue);
+	__NK_ASSERT_ALWAYS(r==KErrNone);
+	}
+
+/**
+  Reset camera hardware to stop capturing images
+*/
+void DCamera1Channel::DoEndCapture()
+	{
+	// For this example test...
+
+	TRACE(Kern::Printf("DCamera1Channel::DoEndCapture\n");)
+
+	// Cancel the timer
+	iCaptureTimer.Cancel();
+	}
+
+/**
+  Setup camera hardware to capture next image
+  Next buffer to fill will be iCaptureBuffers->iNextBuffer;
+
+  @param aLastImage The last image just captured. I.e. the completed capture which caused
+					this method to be called
+*/
+void DCamera1Channel::DoNextCapture()
+	{
+	// For this example test...
+	
+	TRACE(Kern::Printf("DCamera1Channel::DoNextCapture  cur=%08x cnt=%04d nxt=%08x\n",iCaptureBuffers->iCurrentBuffer->iChunkOffset,iCaptureCounter,iCaptureBuffers->iNextBuffer->iChunkOffset);)
+
+	// Put frame counter into current image buffer. (This is the 'image' data we capture).
+	*(TInt*)(iCaptureBuffers->iChunkBase+iCaptureBuffers->iCurrentBuffer->iChunkOffset) = iCaptureCounter++;
+
+	// Restart the timer
+	TInt r = iCaptureTimer.Again(iCaptureRateTicks);
+	if(r==KErrArgument)
+		{
+		// Timer would have already expired.
+		//
+		// In a real device driver this is analogous to iCurrentBuffer already being filled
+		// and the DMA queue being emptied. I.e. we have missed some frames.
+		//
+		// For this test...
+
+		TRACE(Kern::Printf("DCamera1Channel::DoNextCapture frame dropped cnt=%04d\n",iCaptureCounter);)
+
+		// Skip a frame count
+		++iCaptureCounter;
+
+		// Restart timer
+		r = iCaptureTimer.OneShot(iCaptureRateTicks,ETrue);
+		}
+	__NK_ASSERT_ALWAYS(r==KErrNone);
+	}
+