kerneltest/e32test/examples/camera1/camera1_ldd.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 136 743008598095
parent 0 a41df078684a
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// 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);
	}