kernel/eka/drivers/soundsc/soundldd.cpp
changeset 0 a41df078684a
child 15 4122176ea935
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/soundsc/soundldd.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,2999 @@
+// Copyright (c) 2006-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:
+// e32\drivers\soundsc\soundldd.cpp
+// LDD for the shared chunk sound driver.
+// 
+//
+
+/**
+ @file
+ @internalTechnology
+ @prototype
+*/
+
+#include <drivers/soundsc.h>
+#include <kernel/kern_priv.h>
+#include <kernel/cache.h>
+
+//#define USE_PLAY_EOF_TIMER
+
+// Define TEST_WITH_PAGING_CACHE_FLUSHES to flush the paging cache when testing read/writes to user thread in a data-paging system
+//#define TEST_WITH_PAGING_CACHE_FLUSHES
+
+static const char KSoundLddPanic[]="Sound LDD";
+
+LOCAL_C TInt HighestCapabilitySupported(TUint32 aCapsBitField)
+	{
+	TInt n;
+	for (n=31 ; n>=0 ; n--)
+		{
+		if (aCapsBitField&(1<<n))
+			break;
+		}
+	return(n);
+	}
+
+/**
+Standard export function for LDDs. This creates a DLogicalDevice derived object,
+in this case, DSoundScLddFactory.
+*/
+DECLARE_STANDARD_LDD()
+	{
+	return new DSoundScLddFactory;
+	}
+
+/**
+Constructor for the sound driver factory class.
+*/
+DSoundScLddFactory::DSoundScLddFactory()
+	{
+//	iUnitsOpenMask=0;
+
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLddFactory::DSoundScLddFactory"));
+
+	// Set version number for this device.
+	iVersion=RSoundSc::VersionRequired();
+	
+	// Indicate that units / PDD are supported.
+	iParseMask=KDeviceAllowUnit|KDeviceAllowPhysicalDevice;
+	
+	// Leave the units decision to the PDD
+	iUnitsMask=0xffffffff;
+	}
+	
+/**
+Second stage constructor for the sound driver factory class.
+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 DSoundScLddFactory::Install()
+	{
+	return(SetName(&KDevSoundScName));
+	}
+
+/**
+Return the 'capabilities' of the sound driver in general.
+Called in the response to an RDevice::GetCaps() request.
+@param aDes A user-side descriptor to write the capabilities information into.
+*/
+void DSoundScLddFactory::GetCaps(TDes8& aDes) const
+	{
+	// Create a capabilities object
+	TCapsSoundScV01 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 client thread which requested the creation of a logical channel.
+The thread is in a critical section.
+@param aChannel Set by this function to point to the created logical channel.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DSoundScLddFactory::Create(DLogicalChannelBase*& aChannel)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLddFactory::Create"));
+	aChannel=new DSoundScLdd;
+	if (!aChannel)
+		return(KErrNoMemory);
+
+	return(KErrNone);
+	}
+	
+/**
+Check whether a channel has is currently open on the specified unit.
+@param aUnit The number of the unit to be checked.
+@return ETrue if a channel is open on the specified channel, EFalse otherwise.
+@pre The unit info. mutex must be held.
+*/
+TBool DSoundScLddFactory::IsUnitOpen(TInt aUnit)
+	{
+	return(iUnitsOpenMask&(1<<aUnit));
+	}
+
+/**
+Attempt to change the state of the channel open status for a particular channel.
+@param aUnit The number of the unit to be updated.
+@param aIsOpenSetting The required new state for the channel open status: either ETrue to set the status to open or 
+	EFalse to set the status to closed.
+@return KErrNone if the status was updated successfully, KErrInUse if an attempt has been made to set the channnel status
+	to open while it is already open.
+*/		
+TInt DSoundScLddFactory::SetUnitOpen(TInt aUnit,TBool aIsOpenSetting)
+	{
+	NKern::FMWait(&iUnitInfoMutex); // Acquire the unit info. mutex.
+	
+	// Fail a request to open an channel that is already open
+	if (aIsOpenSetting && IsUnitOpen(aUnit))
+		{
+		NKern::FMSignal(&iUnitInfoMutex); // Release the unit info. mutex.
+		return(KErrInUse);
+		}
+	
+	// Update the open status as requested
+	if (aIsOpenSetting)
+		iUnitsOpenMask|=(1<<aUnit);
+	else
+		iUnitsOpenMask&=~(1<<aUnit);
+	
+	NKern::FMSignal(&iUnitInfoMutex); // Release the unit info. mutex.	
+	return(KErrNone);
+	}	
+
+/**
+Constructor for the sound driver logical channel.
+*/
+DSoundScLdd::DSoundScLdd()
+	: iPowerDownDfc(DSoundScLdd::PowerDownDfc,this,3),
+	  iPowerUpDfc(DSoundScLdd::PowerUpDfc,this,3),
+	  iEofTimer(DSoundScLdd::PlayEofTimerExpired,this),
+	  iPlayEofDfc(DSoundScLdd::PlayEofTimerDfc,this,3)
+	{
+//	iDirection=ESoundDirRecord;
+//	iState=EOpen;
+// 	iBufConfig=NULL;
+//	iPowerHandler=NULL;
+//	iSoundConfigFlags=0;
+//	iBytesTransferred=0;
+//	iBufManager=NULL;
+//	iTestSettings=0;
+//	iPlayEofTimerActive=EFalse;
+//	iThreadOpenCount=0;
+
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DSoundScLdd"));
+
+	iUnit=-1;	// Invalid unit number
+	
+	// Many drivers would open the client thread's DThread object here. However, since this driver allows a channel to be shared by multiple client 
+	// threads - we have to open and close the relevent DThread object for each request. 
+	}
+
+/**
+Destructor for the sound driver logical channel.
+*/
+DSoundScLdd::~DSoundScLdd()
+	{
+
+	if (iNotifyChangeOfHwClientRequest)
+		Kern::DestroyClientRequest(iNotifyChangeOfHwClientRequest);
+
+	// Free the TClientRequest structures associated with requests
+	if (iClientRequests)
+		{
+		for (TInt index=0; index<RSoundSc::ERequestRecordData+1; ++index)
+			if (iClientRequests[index])
+				Kern::DestroyClientRequest(iClientRequests[index]);
+
+		delete[] iClientRequests;
+		}
+
+	// Check if we need to delete the shared chunk / audio buffers.
+	if (iBufManager)
+		delete iBufManager;
+	
+	// Delete any memory allocated to hold the current buffer configuration.
+	if (iBufConfig)
+		delete iBufConfig;
+	
+	// Remove and delete the power handler.
+	if (iPowerHandler)
+		{
+		iPowerHandler->Remove(); 
+		delete iPowerHandler;
+		}
+		
+	// Delete the request queue
+	if (iReqQueue)
+		delete iReqQueue;		
+	
+	__ASSERT_DEBUG(iThreadOpenCount==0,Kern::Fault(KSoundLddPanic,__LINE__));	
+	
+	// Clear the 'units open mask' in the LDD factory.
+	if (iUnit>=0)
+		((DSoundScLddFactory*)iDevice)->SetUnitOpen(iUnit,EFalse);
+	}
+	
+/**
+Second stage constructor for the sound driver - called by the kernel's device driver framework.
+This is called in the context of the client thread which requested the creation of a logical channel.
+The thread is in a critical section.
+@param aUnit The unit argument supplied by the client.
+@param aInfo The info argument supplied by the client. Always NULL in this case.
+@param aVer The version argument supplied by the client.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DSoundScLdd::DoCreate(TInt aUnit, const TDesC8* /*aInfo*/, const TVersion& aVer)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoCreate"));
+	
+	// Check the client has ECapabilityMultimediaDD capability.
+	if (!Kern::CurrentThreadHasCapability(ECapabilityMultimediaDD,__PLATSEC_DIAGNOSTIC_STRING("Checked by ESOUNDSC.LDD (Sound driver)")))
+		return(KErrPermissionDenied);
+
+	// Check that the sound driver version specified by the client is compatible.
+	if (!Kern::QueryVersionSupported(RSoundSc::VersionRequired(),aVer))
+		return(KErrNotSupported);
+	
+	// Check that a channel hasn't already been opened on this unit.
+	TInt r=((DSoundScLddFactory*)iDevice)->SetUnitOpen(aUnit,ETrue); // Try to update 'units open mask' in the LDD factory.
+	if (r!=KErrNone)
+		return(r);
+	iUnit=aUnit;
+
+	// Create a TClientRequest for each request that can be completed by the DFC thread.  These TClientRequest
+	// instances are separate to those embedded in the TSoundScRequest structures and are used for requests that
+	// have no associated TSoundScRequest structure or which are completing prematurely before they can be
+	// associated with a TSoundScRequest structure
+	if ((iClientRequests=new TClientRequest*[RSoundSc::ERequestRecordData+1])==NULL)
+		return KErrNoMemory;
+
+	for (TInt index=0; index<RSoundSc::ERequestRecordData+1; ++index)
+		if ((r=Kern::CreateClientRequest(iClientRequests[index]))!=KErrNone)
+			return r;
+
+	if ((r=Kern::CreateClientDataRequest(iNotifyChangeOfHwClientRequest))!=KErrNone)
+		return r;
+
+	// Initialise the PDD
+	Pdd()->iLdd=this;
+	
+	// Read back the capabilities of this device from the PDD and determine the data transfer direction for this unit.
+	TPckg<TSoundFormatsSupportedV02> capsBuf(iCaps);
+	Pdd()->Caps(capsBuf);
+	iDirection=iCaps.iDirection;
+
+	// Check the client has UserEnvironment capability if recording.
+	if(iDirection==ESoundDirRecord)
+		{
+		if (!Kern::CurrentThreadHasCapability(ECapabilityUserEnvironment,__PLATSEC_DIAGNOSTIC_STRING("Checked by ESOUNDSC.LDD (Sound driver)")))
+			return(KErrPermissionDenied);
+		}
+	
+	// Create the appropriate request queue
+	if (iDirection==ESoundDirPlayback)
+		iReqQueue=new TSoundScPlayRequestQueue(this);
+	else
+		iReqQueue=new TSoundScRequestQueue(this);
+	if (!iReqQueue)
+		return(KErrNoMemory);
+	r=iReqQueue->Create();
+	if (r!=KErrNone)
+		return(r);
+	
+	// Setup the default audio configuration acording to these capabilities.
+	iSoundConfig.iChannels=HighestCapabilitySupported(iCaps.iChannels)+1;
+	__ASSERT_ALWAYS(iSoundConfig.iChannels>0,Kern::Fault(KSoundLddPanic,__LINE__));
+	iSoundConfig.iRate=(TSoundRate)HighestCapabilitySupported(iCaps.iRates);
+	__ASSERT_ALWAYS(iSoundConfig.iRate>=0,Kern::Fault(KSoundLddPanic,__LINE__));
+	iSoundConfig.iEncoding=(TSoundEncoding)HighestCapabilitySupported(iCaps.iEncodings);
+	__ASSERT_ALWAYS(iSoundConfig.iEncoding>=0,Kern::Fault(KSoundLddPanic,__LINE__));
+	iSoundConfig.iDataFormat=(TSoundDataFormat)HighestCapabilitySupported(iCaps.iDataFormats);
+	__ASSERT_ALWAYS(iSoundConfig.iDataFormat>=0,Kern::Fault(KSoundLddPanic,__LINE__));
+	__ASSERT_ALWAYS(ValidateConfig(iSoundConfig)==KErrNone,Kern::Fault(KSoundLddPanic,__LINE__));
+	iSoundConfigFlags=0;
+	
+	// Setup the default setting for the record level / play volume.
+	iVolume=KSoundMaxVolume;
+		
+	// Set up the correct DFC queue
+	TDfcQue* dfcq=((DSoundScPdd*)iPdd)->DfcQ(aUnit);
+	SetDfcQ(dfcq);
+	iPowerDownDfc.SetDfcQ(dfcq);
+	iPowerUpDfc.SetDfcQ(dfcq);
+	iMsgQ.Receive();
+	
+	// Create the power handler
+	iPowerHandler=new DSoundScPowerHandler(this);
+	if (!iPowerHandler)
+		return(KErrNoMemory);
+	iPowerHandler->Add();
+	
+	// Power up the hardware.
+	r=Pdd()->PowerUp();
+	
+	return(r);
+	}
+	
+/**
+Shutdown the audio device.
+Terminate all device activity and power down the hardware.
+*/
+void DSoundScLdd::Shutdown()
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::Shutdown"));
+
+	Pdd()->StopTransfer();
+	
+	// Power down the hardware
+	Pdd()->PowerDown();
+
+	// Cancel any requests that we may be handling	
+	DoCancel(RSoundSc::EAllRequests);
+	
+	iState=EOpen;
+
+	// Make sure DFCs and timers are not queued.
+	iPowerDownDfc.Cancel();
+	iPowerUpDfc.Cancel();
+	CancelPlayEofTimer();
+	}
+	
+/**
+Process a request on this logical channel
+Called in the context of the client thread.
+@param aReqNo The request number:
+  	          ==KMaxTInt: a 'DoCancel' message;
+	          >=0: a 'DoControl' message with function number equal to value.
+	          <0: a 'DoRequest' message with function number equal to ~value.
+@param a1 The first request argument. For DoRequest(), this is a pointer to the TRequestStatus.
+@param a2 The second request argument. For DoRequest(), this is a pointer to the 2 actual TAny* arguments.
+@return The result of the request. This is ignored by device driver framework for DoRequest().
+*/ 
+TInt DSoundScLdd::Request(TInt aReqNo, TAny* a1, TAny* a2)
+	{
+//	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::Request(%d)",aReqNo));
+	TInt r;
+	
+	// Check for DoControl or DoRequest functions which are configured to execute in kernel thread context. This
+	// also applies to DoCancel functions and ERequestRecordData requests where recording mode is not yet enabled.
+	if ((aReqNo<RSoundSc::EMsgControlMax && aReqNo>(~RSoundSc::EMsgRequestMax)) ||
+	    aReqNo==KMaxTInt ||
+	    ((~aReqNo)==RSoundSc::ERequestRecordData && (iState==EOpen || iState==EConfigured)) 
+	   )
+		{
+		// Implement in the context of the kernel thread - prepare and issue a kernel message.
+		r=DLogicalChannel::Request(aReqNo,a1,a2);		
+		}	
+	else
+		{
+		// Implement in the context of the client thread.	
+		// Decode the message type and dispatch it to the relevent handler function.
+		if ((TUint)aReqNo<(TUint)KMaxTInt)
+			r=DoControl(aReqNo,a1,a2,&Kern::CurrentThread());	// DoControl - process the request.
+		
+		else
+			{
+			// DoRequest - read the arguments from the client thread and process the request.
+			TAny* a[2];
+			kumemget32(a,a2,sizeof(a)); 
+			TRequestStatus* status=(TRequestStatus*)a1;
+			NKern::ThreadEnterCS(); 				// Need to be in critical section while manipulating the request/buffer list (for record).
+			r=DoRequest(~aReqNo,status,a[0],a[1],&Kern::CurrentThread());
+		
+			// Complete request if there was an error
+			if (r!=KErrNone)
+				CompleteRequest(&Kern::CurrentThread(),status,r);
+			r=KErrNone;
+			NKern::ThreadLeaveCS();
+			}
+		}
+//	__KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::Request - %d",r));
+	return(r);
+	}
+
+/**
+Send a message to the DFC thread for processing by HandleMsg().
+
+This function is called in the context of the client thread.
+
+Overridden to ensure client data is copied kernel-side to avoid page-faults.
+
+@param aMsg  The message to process.
+
+@return KErrNone if the message was send successfully, otherwise one of the other system-wide error
+        codes.
+*/
+TInt DSoundScLdd::SendMsg(TMessageBase* aMsg)
+	{
+	// Executes in context of client thread
+
+	TThreadMessage& m=*(TThreadMessage*)aMsg;
+    TInt id = m.iValue;
+
+	TInt r(KErrNone);
+	if (id == ~RSoundSc::EMsgRequestPlayData)
+		{
+		r = PrePlay(aMsg);
+		if (r!=KErrNone)
+			{
+			// This is an asynchronous request so need to return error through the TRequestStatus
+			TRequestStatus* status = (TRequestStatus*)(m.Ptr0());
+			Kern::RequestComplete(status,r);
+			return(r);
+			}
+		r = DLogicalChannel::SendMsg(aMsg);
+		if (r!=KErrNone)
+			{
+			iReqQueue->Free((TSoundScPlayRequest*)m.iArg[1]);	// Return the unused request object	
+			}
+		return(r);
+		}
+	else if (id == RSoundSc::EMsgControlSetBufChunkCreate || id == RSoundSc::EMsgControlSetBufChunkOpen)
+		{
+		r = PreSetBufferChunkCreateOrOpen(aMsg);
+		if (r!=KErrNone)
+			{
+			return(r);
+			}
+		}
+	else if (id == RSoundSc::EMsgControlSetAudioFormat)
+		{
+		r = PreSetSoundConfig(aMsg);
+		if (r!=KErrNone)
+			{
+			return(r);
+			}
+		}
+
+	r = DLogicalChannel::SendMsg(aMsg);
+		
+
+	return(r);
+	}
+
+/**
+PreProcess a play request on this logical channel
+Called in the context of the client thread.
+
+@param aMsg  The message to process.
+
+@return KErrNone if the parameters are validated and request structure populated. Otherwise a system-wide error.
+*/ 
+TInt DSoundScLdd::PrePlay(TMessageBase* aMsg)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PrePlay"));
+
+	// Executes in context of client thread
+
+	TThreadMessage* m=(TThreadMessage*)aMsg;
+
+	// Copy play information to kernel side before checking
+	SRequestPlayDataInfo info;
+	kumemget(&info,m->iArg[1],sizeof(info));
+
+	__KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::PrePlay - off %x len %x flg %x ",info.iBufferOffset,info.iLength,info.iFlags));
+
+	// validate parameters in the play structure
+
+	// Check that the offset argument is aligned correctly for the PDD.
+	TUint32 alignmask=(1<<iCaps.iRequestAlignment)-1; // iRequestAlignment holds log to base 2 of alignment required
+	if ((info.iBufferOffset & alignmask) != 0)
+		return(KErrArgument);
+	
+	// Check that the length argument is compatible with the minimum request size required for the PDD.
+	if (iCaps.iRequestMinSize && info.iLength%iCaps.iRequestMinSize)
+		return(KErrArgument);
+	
+	// Check that the specified offset and length are valid in the chunk. If so, get a pointer to the corresponding 
+	// audio buffer object.
+	TAudioBuffer* buf;
+	if (iBufManager)
+		{
+		TInt r=iBufManager->ValidateRegion(info.iBufferOffset,info.iLength,buf);
+		if (r!=KErrNone)
+			return(r);
+		}
+	else
+		{
+		return(KErrNotReady);
+		}	
+
+	// Acquire a free request object and add it to the queue of pending requests.
+	TSoundScPlayRequest* req=(TSoundScPlayRequest*)iReqQueue->NextFree();
+	if (!req)
+		return(KErrGeneral);										// Must have exceeded KMaxSndScRequestsPending.
+	req->iTf.Init((TUint)buf,info.iBufferOffset,info.iLength,buf); 	// Use pointer to audio buffer as unique ID
+	req->iFlags=info.iFlags;
+
+	// replace the argument with a pointer to the kernel-side structure
+	m->iArg[1]=req;
+	
+	__KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::PrePlay"));
+
+	return(KErrNone);
+	}
+
+/**
+PreProcess a SetBufferChunkCreate and SetBufferChunkOpen on this logical channel
+Called in the context of the client thread.
+This is synchronous so only need one copy of the data on the kernel-side.
+
+@param aMsg  The message to process.
+
+@return KErrNone if the parameters are validated and request structure populated. Otherwise a system-wide error.
+*/ 
+TInt DSoundScLdd::PreSetBufferChunkCreateOrOpen(TMessageBase* aMsg)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PreSetBufferChunkCreateOrOpen"));
+	TInt r(KErrNone);
+
+	TThreadMessage* m=(TThreadMessage*)aMsg;
+
+	TInt length, maxLength;
+	const TDesC8* userDesc = (const TDesC8*)m->Ptr0();
+	const TUint8* configData = Kern::KUDesInfo(*userDesc,length,maxLength);
+
+	//__KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::PreSetBufferChunkCreateOrOpen - len %x maxlen %x",length,maxLength));
+
+	// check the descriptor length is >= the base class size
+	TInt minDesLen=sizeof(TSharedChunkBufConfigBase);
+	if (length<minDesLen)
+		return(KErrArgument);
+
+	// Temporary copy of client-side buffer config structure  
+	TSharedChunkBufConfigBase chunkBufConfig;
+
+	kumemget(&chunkBufConfig, configData, minDesLen);
+
+	//__KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::PreSetBufferChunkCreateOrOpen - num %x size %x flg %x ",chunkBufConfig.iNumBuffers,chunkBufConfig.iBufferSizeInBytes,chunkBufConfig.iFlags));
+
+	// check the buffer argument
+	if (chunkBufConfig.iNumBuffers<=0)
+		return(KErrArgument);
+
+	// Validate the rest of the configuration supplied.
+	if (chunkBufConfig.iBufferSizeInBytes<=0)
+		return(KErrArgument);
+
+	if (iDirection==ESoundDirRecord)
+		{
+		// If this is a record channel then the size of each buffer must comply with the PDD contraints.
+		if (iCaps.iRequestMinSize && chunkBufConfig.iBufferSizeInBytes%iCaps.iRequestMinSize)
+			return(KErrArgument);
+		}	
+
+	//Allocate space for the buffer list 
+	NKern::ThreadEnterCS();
+	r=ReAllocBufferConfigInfo(chunkBufConfig.iNumBuffers);
+	NKern::ThreadLeaveCS();
+	if (r!=KErrNone)
+		return(r);
+
+	//__KTRACE_OPT(KSOUND1, Kern::Printf("DSoundScLdd::PreSetBufferChunkCreateOrOpen - cfg %x size %x",iBufConfig,iBufConfigSize));
+
+	// copy all data into the buffer list 
+	kumemget(iBufConfig, configData, iBufConfigSize);
+
+	return(r);
+	}
+
+/**
+PreProcess a SetSoundConfig on this logical channel
+Called in the context of the client thread.
+This is synchronous so only need one copy of the data on the kernel-side.
+
+@param aMsg  The message to process.
+
+@return KErrNone if the parameters are validated and request structure populated. Otherwise a system-wide error.
+*/ 
+TInt DSoundScLdd::PreSetSoundConfig(TMessageBase* aMsg)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PreSetSoundConfig"));
+
+	TThreadMessage* m=(TThreadMessage*)aMsg;
+
+	TPtr8 localPtr((TUint8*)&iTempSoundConfig, sizeof(TCurrentSoundFormatV02));
+
+	Kern::KUDesGet(localPtr,*(const TDesC8*)m->Ptr0());
+
+	//__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PreSetSoundConfig chan %x rate %x enc %x form %x",
+	//	iTempSoundConfig.iChannels,iTempSoundConfig.iRate,iTempSoundConfig.iEncoding,iTempSoundConfig.iDataFormat));
+
+	// Check that it is compatible with this sound device.
+	TInt r=ValidateConfig(iTempSoundConfig);
+	
+	return(r);
+	}
+
+/**
+Processes a message for this logical channel.
+This function is called in the context of a DFC thread.
+@param aMsg The message to process.
+	        The iValue member of this distinguishes the message type:
+	          iValue==ECloseMsg: channel close message.
+	          iValue==KMaxTInt: a 'DoCancel' message
+	          iValue>=0: a 'DoControl' message with function number equal to iValue
+	          iValue<0: a 'DoRequest' message with function number equal to ~iValue
+*/
+void DSoundScLdd::HandleMsg(TMessageBase* aMsg)
+	{
+#ifdef _DEBUG
+#ifdef TEST_WITH_PAGING_CACHE_FLUSHES
+	Kern::SetRealtimeState(ERealtimeStateOn);  
+	Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
+#endif
+#endif
+
+	TThreadMessage& m=*(TThreadMessage*)aMsg;
+    TInt id=m.iValue;
+//	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::HandleMsg(%d)",id));
+    
+	if (id==(TInt)ECloseMsg)
+		{
+		// Channel close.
+		Shutdown();
+		m.Complete(KErrNone,EFalse);
+		return;
+		}
+    else if (id==KMaxTInt)
+		{
+		// DoCancel
+		DoCancel(m.Int0());
+		m.Complete(KErrNone,ETrue);
+		return;
+		}
+    else if (id<0)
+		{
+		// DoRequest
+		TRequestStatus* pS=(TRequestStatus*)m.Ptr0();
+		TInt r=DoRequest(~id,pS,m.Ptr1(),m.Ptr2(),m.Client());
+		if (r!=KErrNone)
+			{
+			iClientRequests[~id]->SetStatus(pS);
+			CompleteRequest(m.Client(),NULL,r,iClientRequests[~id]);
+			}
+		m.Complete(KErrNone,ETrue);
+		}
+    else
+		{
+		// DoControl
+		TInt r=DoControl(id,m.Ptr0(),m.Ptr1(),m.Client());
+		m.Complete(r,ETrue);
+		}
+	}
+
+/**
+Process a synchronous 'DoControl' request.
+@param aFunction The request number.
+@param a1 The first request argument.
+@param a2 The second request argument.
+@param aThread The client thread which issued the request.
+@return The result of the request.
+*/
+TInt DSoundScLdd::DoControl(TInt aFunction,TAny* a1,TAny* a2,DThread* aThread)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoControl(%d)",aFunction));
+	
+	TInt r=KErrNotSupported;
+	switch (aFunction)
+		{
+		case RSoundSc::EControlGetCaps:
+			{
+			// Return the capabilities for this device. Read this from the PDD and 
+			// then write it to the client. 
+			TSoundFormatsSupportedV02Buf caps;
+			Pdd()->Caps(caps);
+			Kern::InfoCopy(*((TDes8*)a1),caps);
+			r=KErrNone;
+			break;	
+			}
+		case RSoundSc::EControlGetAudioFormat:
+			{
+			// Write the current audio configuration back to the client.
+			TPtrC8 ptr((const TUint8*)&iSoundConfig,sizeof(iSoundConfig));
+			Kern::InfoCopy(*((TDes8*)a1),ptr);
+			r=KErrNone;
+			break;	
+			}
+		case RSoundSc::EMsgControlSetAudioFormat:
+			{
+			if (iState==EOpen || iState==EConfigured || iPlayEofTimerActive)
+				{
+				// If the play EOF timer is active then it is OK to change the audio configuration - but we
+				// need to bring the PDD out of transfer mode first.
+				if (iPlayEofTimerActive)
+					{
+					CancelPlayEofTimer();
+					Pdd()->StopTransfer();
+					}
+				
+				r=SetSoundConfig(); 
+				if (r==KErrNone && (iSoundConfigFlags&KSndScVolumeIsSetup) && iBufConfig)
+					iState=EConfigured;
+				}		
+			else
+				r=KErrInUse;
+			break;
+			}
+		case RSoundSc::EControlGetBufConfig:
+			if (iBufConfig)
+				{
+				// Write the buffer config to the client.
+				TPtrC8 ptr((const TUint8*)iBufConfig,iBufConfigSize);
+				Kern::InfoCopy(*((TDes8*)a1),ptr);
+				r=KErrNone;	
+				}	
+			break;
+		case RSoundSc::EMsgControlSetBufChunkCreate:
+			{
+			if (iState==EOpen || iState==EConfigured || iPlayEofTimerActive)
+				{
+				// Need to be in critical section while deleting an exisiting config and creating a new one
+				NKern::ThreadEnterCS();
+				r=SetBufferConfig(aThread);
+				NKern::ThreadLeaveCS();
+				if (r==KErrNone && (iSoundConfigFlags&KSndScSoundConfigIsSetup) && (iSoundConfigFlags&KSndScVolumeIsSetup))
+					iState=EConfigured; 		
+				}
+			else
+				r=KErrInUse;
+			break;
+			}
+		case RSoundSc::EMsgControlSetBufChunkOpen:
+			{
+			if (iState==EOpen || iState==EConfigured || iPlayEofTimerActive)
+				{
+				// Need to be in critical section while deleting an exisiting config and creating a new one
+				NKern::ThreadEnterCS();
+				r=SetBufferConfig((TInt)a2,aThread);
+				NKern::ThreadLeaveCS();
+				if (r==KErrNone && (iSoundConfigFlags&KSndScSoundConfigIsSetup) && (iSoundConfigFlags&KSndScVolumeIsSetup))
+					iState=EConfigured; 		
+				}
+			else
+				r=KErrInUse;
+			break;
+			}
+		case RSoundSc::EControlGetVolume:
+			r=iVolume;
+			break;
+		case RSoundSc::EMsgControlSetVolume:
+			{
+			r=SetVolume((TInt)a1);
+			if (r==KErrNone && iState==EOpen && (iSoundConfigFlags&KSndScSoundConfigIsSetup) && iBufConfig)
+				iState=EConfigured;
+			break;	
+			}
+		case RSoundSc::EMsgControlCancelSpecific:
+			{
+			if (iDirection==ESoundDirPlayback)
+				{
+				// Don't try to cancel a play transfer that has already started - let it complete in its own time.
+				TSoundScPlayRequest* req=(TSoundScPlayRequest*)iReqQueue->Find((TRequestStatus*)a1);
+				if (req && req->iTf.iTfState==TSndScTransfer::ETfNotStarted)
+					{
+					iReqQueue->Remove(req);
+					CompleteRequest(req->iOwningThread,NULL,KErrCancel,req->iClientRequest);
+					iReqQueue->Free(req);
+					}
+				}
+			else
+				{
+				// Need to aquire the buffer/request list mutex when removing record requests - RecordData() runs in
+				// client thread context and this may access the queue. Record requests a treated differently to play
+				// requests and you don't have to worry about record requests already being in progress.
+				NKern::FMWait(&iMutex);
+				TSoundScRequest* req=iReqQueue->Find((TRequestStatus*)a1);
+				if (req)
+					{
+					iReqQueue->Remove(req);
+					DThread* thread=req->iOwningThread;				// Take a copy before we free it.
+					TClientRequest* clreq=req->iClientRequest;		// Take a copy before we free it.
+					NKern::FMSignal(&iMutex);
+					iReqQueue->Free(req);
+					CompleteRequest(thread,NULL,KErrCancel,clreq);
+					}
+				else
+					NKern::FMSignal(&iMutex);	
+				}
+			r=KErrNone;	
+			break;	
+			}
+		case RSoundSc::EControlBytesTransferred:
+			r=iBytesTransferred;
+			break;
+		case RSoundSc::EControlResetBytesTransferred:
+			iBytesTransferred=0;
+			r=KErrNone;	
+			break;
+		case RSoundSc::EMsgControlPause:
+			if (iState==EActive)
+				{
+				// Have to update the status early here because a record PDD may call us back with RecordCallback() in
+				// handling PauseTransfer() - to complete a partially filled buffer.  
+				iState=EPaused;
+				iCompletesWhilePausedCount=0;		
+				r=Pdd()->PauseTransfer();
+				if (r!=KErrNone)
+					iState=EActive;
+				else if (iDirection==ESoundDirRecord)
+					{
+					// For record, complete any pending record requests that are still outstanding following PauseTransfer().
+					iReqQueue->CompleteAll(KErrCancel);	
+					}
+				}
+			else
+				r=KErrNotReady;	
+			break;
+		case RSoundSc::EMsgControlResume:
+			if (iState==EPaused)
+				{
+				r=Pdd()->ResumeTransfer();
+				if (r==KErrNone && iDirection==ESoundDirRecord)
+					r=StartNextRecordTransfers();
+				if (r==KErrNone)
+					iState=EActive;	// Successfully resumed transfer - update the status.
+				}
+			else
+				r=KErrNotReady;	
+			break;
+		case RSoundSc::EControlReleaseBuffer:
+			if (iDirection==ESoundDirRecord)
+				r=ReleaseBuffer((TInt)a1);
+			break; 
+		case RSoundSc::EMsgControlCustomConfig:
+			r=CustomConfig((TInt)a1,a2);
+			break;
+		case RSoundSc::EControlTimePlayed:
+			if (iDirection==ESoundDirPlayback)
+				{
+				TInt64 time=0;
+				r=Pdd()->TimeTransferred(time,iState);
+				TPtrC8 timePtr((TUint8*)&time,sizeof(TInt64));
+				Kern::ThreadDesWrite(aThread,a1,timePtr,0,KTruncateToMaxLength,NULL);
+				}
+			else
+				r=KErrNotSupported;
+			break;
+		case RSoundSc::EControlTimeRecorded:
+			if (iDirection==ESoundDirRecord)
+				{
+				TInt64 time=0;
+				r=Pdd()->TimeTransferred(time,iState);
+				TPtrC8 timePtr((TUint8*)&time,sizeof(TInt64));
+				Kern::ThreadDesWrite(aThread,a1,timePtr,0,KTruncateToMaxLength,NULL);
+				}
+			else
+				r=KErrNotSupported;
+			break;
+		}
+		
+	__KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::DoControl - %d",r));
+	return(r);
+	}
+
+/**
+Process an asynchronous 'DoRequest' request.
+@param aFunction The request number.
+@param aStatus A pointer to the TRequestStatus.
+@param a1 The first request argument.
+@param a2 The second request argument.
+@param aThread The client thread which issued the request.
+@return The result of the request.
+*/
+TInt DSoundScLdd::DoRequest(TInt aFunction, TRequestStatus* aStatus, TAny* a1, TAny* /*a2*/,DThread* aThread)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoRequest(%d)",aFunction));
+	
+	// Open a reference on the client thread while the request is pending so it's control block can't disappear until this driver has finished with it.
+	TInt r=aThread->Open();
+	__ASSERT_ALWAYS(r==KErrNone,Kern::Fault(KSoundLddPanic,__LINE__));
+#ifdef _DEBUG
+	__e32_atomic_add_ord32(&iThreadOpenCount, 1);
+#endif		
+
+	r=KErrNotSupported;
+	switch (aFunction)
+		{
+		case RSoundSc::EMsgRequestPlayData:
+			{
+			if (iDirection==ESoundDirPlayback)
+				{
+				if (iState==EOpen)
+					{
+					// Not yet fully configured - maybe we can use the default settings.
+					r=KErrNone;
+					if (!iBufConfig)
+						r=KErrNotReady;	// Can't guess a default buffer configuration.
+					else
+						{
+						if (!(iSoundConfigFlags&KSndScSoundConfigIsSetup))
+							r=DoSetSoundConfig(iSoundConfig);	// Apply default sound configuration.
+						if (r==KErrNone && !(iSoundConfigFlags&KSndScVolumeIsSetup))
+							r=SetVolume(iVolume);				// Apply default volume level
+						}
+					if (r!=KErrNone)
+						break;
+					else
+						iState=EConfigured;		
+					}
+					
+				if (iState==EConfigured || iState==EActive || iState==EPaused)
+					{
+					r=PlayData(aStatus, (TSoundScPlayRequest*)a1,aThread);
+					}
+				else
+					r=KErrNotReady;
+				}
+			break;
+			}
+		case RSoundSc::ERequestRecordData:
+			if (iDirection==ESoundDirRecord)
+				{
+				// Check if the device has been configured yet
+				if (iState==EOpen)
+					{
+					// Not yet fully configured - maybe we can use the default settings.
+					r=KErrNone;
+					if (!iBufConfig)
+						r=KErrNotReady;	// Can't guess a default buffer configuration.
+					else
+						{
+						if (!(iSoundConfigFlags&KSndScSoundConfigIsSetup))
+							r=DoSetSoundConfig(iSoundConfig);	// Apply default sound configuration.
+						if (r==KErrNone && !(iSoundConfigFlags&KSndScVolumeIsSetup))
+							r=SetVolume(iVolume);				// Apply default volume level
+						}
+					if (r!=KErrNone)
+						break;
+					else
+						iState=EConfigured;		
+					}
+				// Check if we need to start recording
+				if (iState==EConfigured)
+					{
+					r=StartRecord();
+					if (r!=KErrNone)
+						break;
+					else
+						iState=EActive;
+					}	
+				
+				// State must be either active or paused so process the record request as appropriate for these states.
+				r=RecordData(aStatus,(TInt*)a1,aThread);
+				}
+			break;
+		case RSoundSc::ERequestNotifyChangeOfHwConfig:
+			{
+			// Check if this device can detect changes in its hardware configuration.
+			if (iCaps.iHwConfigNotificationSupport)
+				{
+				r=KErrNone;
+				if (!iNotifyChangeOfHwClientRequest->IsReady())
+					{
+					iChangeOfHwConfigThread=aThread;
+					iNotifyChangeOfHwClientRequest->SetDestPtr((TBool*)a1);
+					r = iNotifyChangeOfHwClientRequest->SetStatus(aStatus);
+					}
+				else
+					r=KErrInUse;
+				}
+			else
+				r=KErrNotSupported;	
+			break;
+			}
+		}
+		
+	__KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::DoRequest - %d",r));
+	return(r);
+	}
+
+/**
+Process the cancelling of asynchronous requests.
+@param aMask A mask indicating which requests need to be cancelled.
+@return The result of the cancel.
+*/
+TInt DSoundScLdd::DoCancel(TUint aMask)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoCancel(%08x)",aMask));
+
+	if (aMask&(1<<RSoundSc::EMsgRequestPlayData))
+		{
+		Pdd()->StopTransfer();
+		iReqQueue->CompleteAll(KErrCancel);					// Cancel any outstanding play requests
+		if ((iState==EActive)||(iState==EPaused))
+			iState=EConfigured;
+		}
+	if (aMask&(1<<RSoundSc::ERequestRecordData))
+		{
+		Pdd()->StopTransfer();
+		iReqQueue->CompleteAll(KErrCancel,&iMutex);		// Cancel any outstanding record requests
+		if ((iState==EActive)||(iState==EPaused))
+			iState=EConfigured;
+		}
+	if (aMask&(1<<RSoundSc::ERequestNotifyChangeOfHwConfig))
+		{
+		// Complete any pending hardware change notifier with KErrCancel.
+		if (iNotifyChangeOfHwClientRequest->IsReady())
+			CompleteRequest(iChangeOfHwConfigThread,NULL,KErrCancel,iNotifyChangeOfHwClientRequest); 
+		}		
+	return(KErrNone);
+	}
+
+/**
+Set the current buffer configuration - creating a shared chunk.
+@param aBufferConfigBuf A packaged TSharedChunkBufConfigBase derived object holding the buffer configuration settings of
+	the shared chunk required.
+@param aThread The client thread which has requested to own the chunk.
+@return A handle to the shared chunk for the owning thread (a value >0), if successful;
+        otherwise one of the other system wide error codes, (a value <0).
+@pre The thread must be in a critical section. 
+*/	
+TInt DSoundScLdd::SetBufferConfig(DThread* aThread)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:SetBufferConfig"));
+	
+	TInt r(KErrNone);
+
+	// Delete any existing buffers and the shared chunk.
+	if (iBufManager)
+		{
+		delete iBufManager;
+		iBufManager=NULL;
+		} 
+						
+	// If a handle to the shared chunk was created, close it, using the handle of the thread on which
+	// it was created, in case a different thread is now calling us
+	if (iChunkHandle>0)
+		{
+		Kern::CloseHandle(iChunkHandleThread,iChunkHandle);
+		iChunkHandle=0;
+		}
+
+	// Create the shared chunk, then create buffer objects for the committed buffers within it. This is
+	// done by creating a buffer manager - create the apppropraiate version according to the audio direction.
+	if (iDirection==ESoundDirPlayback)
+		iBufManager=new DBufferManager(this);
+	else
+		iBufManager=new DRecordBufferManager(this);
+	if (!iBufManager)
+		return(KErrNoMemory);
+	r=iBufManager->Create(iBufConfig);
+	if (r!=KErrNone)
+		{
+		delete iBufManager;
+		iBufManager=NULL;
+		return(r);
+		} 
+	
+	// Create handle to the shared chunk for the owning thread.
+	r=Kern::MakeHandleAndOpen(aThread,iBufManager->iChunk);
+
+	// And save the the chunk and thread handles for later.  Normally the chunk handle will be closed when the chunk
+	// is closed, but if the chunk is re-allocated then it will need to be closed before re-allocation.
+	iChunkHandle=r;
+	iChunkHandleThread=aThread;
+
+	return(r);
+	}	
+
+/**
+Set the current buffer configuration - using an existing shared chunk.
+@param aBufferConfigBuf A packaged TSharedChunkBufConfigBase derived object holding the buffer configuration settings of
+	the shared chunk supplied.
+@param aChunkHandle A handle for the shared chunk supplied by the client.
+@param aThread The thread in which the given handle is valid.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+@pre The thread must be in a critical section. 
+*/	
+TInt DSoundScLdd::SetBufferConfig(TInt aChunkHandle,DThread* aThread)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:SetBufferConfig(Handle-%d)",aChunkHandle));
+
+	TInt r(KErrNone);
+
+	// Delete any existing buffers and the shared chunk.
+	if (iBufManager)
+		{
+		delete iBufManager;
+		iBufManager=NULL;
+		} 
+	
+	// Open the shared chunk supplied and create buffer objects for the committed buffers within it. This is
+	// done by creating a buffer manager - create the apppropraiate version according to the audio direction.
+	if (iDirection==ESoundDirPlayback)
+		iBufManager=new DBufferManager(this);
+	else
+		iBufManager=new DRecordBufferManager(this);
+	if (!iBufManager)
+		return(KErrNoMemory);
+	r=iBufManager->Create(*iBufConfig,aChunkHandle,aThread);
+	if (r!=KErrNone)
+		{
+		delete iBufManager;
+		iBufManager=NULL;
+		} 
+	return(r);
+	}	
+
+/**
+Set the current audio format configuration.
+@param aSoundConfigBuf A packaged sound configuration object holding the new audio configuration settings to be used.
+@param aThread The client thread which contains the sound configuration object.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DSoundScLdd::SetSoundConfig()
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:SetSoundConfig"));
+	
+	TInt r=DoSetSoundConfig(iTempSoundConfig);
+
+	__KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::SetSoundConfig - %d",KErrNone));
+	return(r);
+	}
+
+/**
+Apply a new audio format configuration.
+@param aSoundConfig A reference to a sound configuration object holding the new audio configuration settings to be applied.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/	
+TInt DSoundScLdd::DoSetSoundConfig(const TCurrentSoundFormatV02& aSoundConfig)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:DoSetSoundConfig"));
+		
+	// We're about to replace any previous configuration - so set the
+	// status back to un-configured in case we don't succeed with the new one.
+	iSoundConfigFlags&=~KSndScSoundConfigIsSetup;
+	
+	// Call the PDD to change the hardware configuration according to the new specification.
+	// Pass it as a descriptor - to support future changes to the config structure.
+	TPtrC8 ptr((TUint8*)&aSoundConfig,sizeof(aSoundConfig));
+	TInt r=Pdd()->SetConfig(ptr);
+	if (r!=KErrNone)
+		return(r);
+	
+	// Setting up the new play configuration has succeeded so save the new configuration.
+	iSoundConfig=aSoundConfig;
+	iSoundConfigFlags|=KSndScSoundConfigIsSetup;
+	
+	// For some devices, the maximum transfer length supported will vary according to the configuration.
+	if (iBufManager)
+		iBufManager->iMaxTransferLen=Pdd()->MaxTransferLen();
+	
+	return(r);
+	}	
+	
+/**
+Set the current play volume or record level.
+@param aVolume The play volume / record level to be set - a value in the range 0 to 255. The value 255 equates to
+	the maximum volume and each value below this equates to a 0.5dB step below it.
+@return KErrNone if successful, otherwise one of the other system wide error codes.	
+*/
+TInt DSoundScLdd::SetVolume(TInt aVolume)
+	{
+	TInt r;
+	// Check if the volume specified is in range.
+	if (aVolume>=0 && aVolume<=255)
+		{
+		// Check if we need to change it.
+		if (!(iSoundConfigFlags&KSndScVolumeIsSetup) || aVolume!=iVolume)
+			{
+			// We're about to replace any previous volume setting - so set the
+			// status back to un-set in case we don't succeed with the new setting.
+			iSoundConfigFlags&=~KSndScVolumeIsSetup;
+			
+			r=Pdd()->SetVolume(aVolume);
+			if (r==KErrNone)
+				{
+				iVolume=aVolume;
+				iSoundConfigFlags|=KSndScVolumeIsSetup;
+				}
+			}
+		else
+			r=KErrNone;	
+		}
+	else
+		r=KErrArgument;	
+	__KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::SetVolume(%d) - %d",aVolume,r));
+	return(r);
+	}
+	
+/**
+Handle a play request from the client.
+@param aStatus The request status to be signalled when the play request is complete.
+@param aChunkOffset Offset from the beginning of the play chunk for the start of data to be played.
+@param aLength The number of bytes of data to be played.
+@param aFlags The play request flags which were supplied by the client for this request.
+@param aThread The client thread which issued the request and which supplied the request status.
+@return KErrNone if successful;
+        KErrArgument if the offset or length arguments are not fully contained within a buffer or don't meet the
+        	alignment contraints of the PDD;
+        KErrNoMemory if a memory error was ecountered in the handling of this request.
+        otherwise one of the other system-wide error codes.
+*/
+TInt DSoundScLdd::PlayData(TRequestStatus* aStatus,TSoundScPlayRequest* aRequest,DThread* aThread)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:PlayData(off:%x len:%d)",aRequest->iTf.GetStartOffset(),aRequest->iTf.GetNotStartedLen()));
+	
+	// Purge the region of the play chunk concerned.
+	iBufManager->FlushData(aRequest->iTf.GetStartOffset(),aRequest->iTf.GetNotStartedLen(),DBufferManager::EFlushBeforeDmaWrite);
+	
+	
+	TInt r(KErrNone);
+
+	// finalise the request data here
+	r = aRequest->iClientRequest->SetStatus(aStatus);
+	if (r!=KErrNone)
+		return(r);
+
+	aRequest->iOwningThread = aThread;
+
+
+	// Check whether we have started the codec yet.
+	CancelPlayEofTimer();
+	if (iState==EConfigured)
+		{
+		r=Pdd()->StartTransfer();
+	
+		// Test settings - only possible in debug mode. Test handling of an error returned from the PDD for StartTransfer().
+#ifdef _DEBUG	
+		if (iTestSettings & KSoundScTest_StartTransferError)
+			{
+			iTestSettings&=(~KSoundScTest_StartTransferError);
+			r=KErrTimedOut;
+			// Any time that StartTransfer() is called on the PDD it must have a matching StopTransfer() before
+			// it is called again
+			Pdd()->StopTransfer();
+			}
+#endif
+		}
+	
+	if (r==KErrNone)
+		{
+		// No further error is possible at this stage so add the request to the queue.
+		iReqQueue->Add(aRequest);
+		
+		if (iState!=EPaused)
+			{
+			iState=EActive;	
+			StartNextPlayTransfers(); // Queue as many transfer requests on the PDD as it can accept.
+			}
+		}
+	else
+		iReqQueue->Free(aRequest);	// Return the unused request object	
+	
+	return(r);
+	}
+	
+/**
+@publishedPartner
+@prototype
+
+Called from the PDD each time it has completed a data transfer from a play buffer.  This function must be called
+in the context of the DFC thread used for processing requests.
+The function performed here is to check whether the entire transfer for the current request is now complete. Also to
+queue further requests on the PDD which should now have the capability to accept more transfers. If the current
+request is complete then we signal completion to the client.
+@param aTransferID A value provided by the LDD when it initiated the transfer allowing the transfer fragment to be 
+	uniquely identified.
+@param aTransferResult The result of the transfer being completed: KErrNone if successful, otherwise one of the other
+	system wide error codes.
+@param aBytesPlayed The number of bytes played from the play buffer.	
+*/
+void DSoundScLdd::PlayCallback(TUint aTransferID,TInt aTransferResult,TInt aBytesPlayed)
+	{
+#ifdef _DEBUG
+#ifdef TEST_WITH_PAGING_CACHE_FLUSHES
+	Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
+#endif
+#endif
+	// Test settings - only possible in debug mode.
+#ifdef _DEBUG	
+	if (iTestSettings & KSoundScTest_TransferDataError)
+		{
+		iTestSettings&=(~KSoundScTest_TransferDataError);
+		aTransferResult=KErrTimedOut;
+		}
+#endif
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::PlayCallback(ID:%xH,Len:%d) - %d",aTransferID,aBytesPlayed,aTransferResult));		
+	
+	// The PDD has completed transfering a fragment. Find the associated request from its ID
+	TBool isNextToComplete;
+	TSoundScPlayRequest* req=((TSoundScPlayRequestQueue*)iReqQueue)->Find(aTransferID,isNextToComplete);
+	
+	// Check if this is a fragment from an earlier request which failed - which we should ignore. This is the case if the request cannot be found 
+	// (because it was already completed back to client) or if the request status is already set as 'done'. 
+	if (req && req->iTf.iTfState!=TSndScTransfer::ETfDone)
+		{
+		__ASSERT_DEBUG(req->iTf.iTfState!=TSndScTransfer::ETfNotStarted,Kern::Fault(KSoundLddPanic,__LINE__));
+		
+		// Update the count of bytes played.
+		iBytesTransferred+=aBytesPlayed;
+		
+		if (aTransferResult!=KErrNone)
+			{
+			// Transfer failed - immediately mark the request as being complete.
+			req->SetFail(aTransferResult);
+			}
+		else
+			req->UpdateProgress(aBytesPlayed);	// Transfer successful so update the progress of the request.
+									
+		// If we have just played an entire request and the PDD has not signalled it ahead of any earlier unfinished ones then complete it back to client.
+		if (req->iTf.iTfState==TSndScTransfer::ETfDone && isNextToComplete)
+			CompleteAllDonePlayRequests(req);
+		}
+	
+	// PDD should now have the capacity to accept another transfer so queue as many transfers
+	// on it as it can accept.
+	StartNextPlayTransfers();
+		
+	
+	return;
+	}
+	
+/**
+This function checks whether there are any outstanding play requests. While there are, it breaks these down into
+transfers sizes which are compatible with the PDD and then repeatedly attempts to queue these data transfers on the
+PDD until it indicates that it can accept no more for the moment.
+@post Data transfer may be stopped in the PDD and the operating state of the channel moved back to EConfigured.
+*/
+void DSoundScLdd::StartNextPlayTransfers()
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::StartNextPlayTransfers"));
+	
+	// Queue as many transfers on the PDD as it can accept.
+	TSoundScPlayRequest* req;
+	TInt r=KErrNone;
+	while (r==KErrNone && (req=((TSoundScPlayRequestQueue*)iReqQueue)->NextRequestForTransfer())!=NULL)
+		{
+		TInt pos=req->iTf.GetStartOffset();
+		TPhysAddr physAddr;
+		TInt len=req->iTf.iAudioBuffer->GetFragmentLength(pos,req->iTf.GetNotStartedLen(),physAddr);
+		if (len>0)
+			{
+			r=Pdd()->TransferData(req->iTf.iId,(iBufManager->iChunkBase+pos),physAddr,len);
+			__KTRACE_OPT(KSOUND1, Kern::Printf("<PDD:TransferData(off:%x len:%d) - %d",pos,len,r));
+			if (r==KErrNone)
+				req->iTf.SetStarted(len);	// Successfully queued a transfer - update the request status.
+			else if (r!=KErrNotReady)
+				{
+				// Transfer error from PDD, fail the request straight away. (Might not be the one at the head of queue).	
+				CompletePlayRequest(req,r);
+				}	
+			}
+		else
+			{
+			// This can only be a zero length play request - just complete it straight away
+			CompletePlayRequest(req,KErrNone);	
+			}
+		} 
+	return;	
+	}
+
+/**
+Complete a client play request back to the client and remove it from the request queue.
+@param aReq A pointer to the play request object to be completed.
+@post Data transfer may be stopped in the PDD and the operating state of the channel moved back to EConfigured.
+*/	
+void DSoundScLdd::DoCompletePlayRequest(TSoundScPlayRequest* aReq)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::DoCompletePlayRequest(%x) - %d",aReq,aReq->iCompletionReason));
+	
+	iReqQueue->Remove(aReq);
+	
+	// If the request queue is now empty then turn off the codec
+	if (iReqQueue->IsEmpty())
+		{
+#ifdef USE_PLAY_EOF_TIMER
+		StartPlayEofTimer();
+#else
+		Pdd()->StopTransfer();
+		iState=EConfigured;
+#endif						
+		// This is an underflow situation.
+		if (aReq->iCompletionReason==KErrNone && aReq->iFlags!=KSndFlagLastSample)
+			aReq->iCompletionReason=KErrUnderflow;
+		}
+		
+	CompleteRequest(aReq->iOwningThread,NULL,aReq->iCompletionReason,aReq->iClientRequest);
+	iReqQueue->Free(aReq);
+	return;	
+	}
+
+/**
+Complete one or more play requests. This function completes the play request specified. It also completes any other play 
+requests which immediately follow the one specified in the play request queue and for which transfer has been completed by the PDD.
+@param aReq A pointer to the play request object to be completed.
+@post Data transfer may be stopped in the PDD and the operating state of the channel moved back to EConfigured.
+*/		
+void DSoundScLdd::CompleteAllDonePlayRequests(TSoundScPlayRequest* aReq)
+	{
+	TSoundScPlayRequest* nextReq=aReq;
+	TSoundScPlayRequest* req;
+	do 
+		{
+		req=nextReq;
+		nextReq=(TSoundScPlayRequest*)req->iNext;
+		DoCompletePlayRequest(req);
+		}
+	while (!iReqQueue->IsAnchor(nextReq) && nextReq->iTf.iTfState==TSndScTransfer::ETfDone);
+	return;	
+	}	
+		
+/**
+Start the audio device recording data.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/
+TInt DSoundScLdd::StartRecord()
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::StartRecord"));
+		
+	// Reset all the audio buffer lists 
+	NKern::FMWait(&iMutex); 		// Acquire the buffer/request list mutex.
+	((DRecordBufferManager*)iBufManager)->Reset();
+	NKern::FMSignal(&iMutex); 		// Release the buffer/request list mutex.
+	
+	// Reset the transfer status for the current and pending record buffers.
+	TAudioBuffer* buf=((DRecordBufferManager*)iBufManager)->GetCurrentRecordBuffer();
+	iCurrentRecBufTf.Init((TUint)buf,buf->iChunkOffset,buf->iSize,buf);		// Use pointer to record buffer as unique ID
+	buf=((DRecordBufferManager*)iBufManager)->GetNextRecordBuffer();
+	iNextRecBufTf.Init((TUint)buf,buf->iChunkOffset,buf->iSize,buf);		// Use pointer to record buffer as unique ID
+	
+	// Call the PDD to prepare the hardware for recording.
+	TInt r=Pdd()->StartTransfer();
+	
+	// Test settings - only possible in debug mode. Test handling of an error returned from the PDD for StartTransfer().
+#ifdef _DEBUG	
+	if (iTestSettings & KSoundScTest_StartTransferError)
+		{
+		iTestSettings&=(~KSoundScTest_StartTransferError);
+		r=KErrTimedOut;
+		}
+#endif		
+	
+	// Initiate data transfer into the first record buffer(s).	
+	if (r==KErrNone)
+		r=StartNextRecordTransfers();
+	return(r);	
+	}
+	
+/**
+Handle a record request from the client once data transfer has been intiated.
+@param aStatus The request status to be signalled when the record request is complete. If the request is successful
+   then this is set to the offset within the shared chunk where the record data resides. Alternatively, if an error 
+   occurs, it will be set to one of the system wide error values.
+@param aLengthPtr A pointer to a TInt object in client memory. On completion, the number of bytes successfully
+   recorded are written to this object.
+@param aThread The client thread which issued the request and which supplied the request status.    	
+@return KErrNone if successful;
+		KErrInUse: if the client needs to free up record buffers before further record requests can be accepted;
+		KErrCancel: if the driver is in paused mode and there are no complete or partially full buffers to return.
+        otherwise one of the other system-wide error codes.
+*/
+TInt DSoundScLdd::RecordData(TRequestStatus* aStatus,TInt* aLengthPtr,DThread* aThread)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd:RecordData"));
+	
+	TInt r=KErrNone;
+
+	NKern::FMWait(&iMutex); 		// Acquire the buffer/request list mutex.
+	 
+	// Check if we have had an overflow since the last record request was completed.
+	if (((DRecordBufferManager*)iBufManager)->iBufOverflow)
+		{
+		((DRecordBufferManager*)iBufManager)->iBufOverflow=EFalse;
+		NKern::FMSignal(&iMutex); 	// Release the buffer/request list mutex.	
+		return(KErrOverflow);	
+		}
+	
+	// See if there is a buffer already available.
+	TAudioBuffer* buf=((DRecordBufferManager*)iBufManager)->GetBufferForClient();
+	if (buf)
+		{
+		// There is an buffer available already - complete the request returning the offset of the buffer to the client.
+		NKern::FMSignal(&iMutex); 	// Release the buffer/request list mutex.
+		
+		r=buf->iResult;
+
+		if (r==KErrNone)
+			{
+			kumemput(aLengthPtr,&buf->iBytesAdded,sizeof(TInt));
+			// Only complete if successful here. Errors will be completed on returning from this method.
+			CompleteRequest(aThread,aStatus,(buf->iChunkOffset));
+			}
+		return(r);	
+		}
+	
+	// If we are paused and there was no un-read data to return to the client then return KErrCancel to prompt them to resume.
+	if (iState==EPaused)
+		{
+		NKern::FMSignal(&iMutex); 	// Release the buffer/request list mutex.
+		return(KErrCancel);
+		}			
+		
+	// The buffer 'completed' list is empty. If the buffer 'free' list is empty too then the client needs
+	// to free some buffers up - return an error.
+	if (((DRecordBufferManager*)iBufManager)->iFreeBufferQ.IsEmpty())
+		{
+		NKern::FMSignal(&iMutex); 	// Release the buffer/request list mutex.	
+		return(KErrInUse);	
+		}	
+	
+	// Acquire a new request object and add it to the queue of pending requests. The request will be completed
+	// from the PDD and the DFC thread when a buffer is available.
+	NKern::FMSignal(&iMutex);
+	TSoundScRequest* req=iReqQueue->NextFree();
+	NKern::FMWait(&iMutex);
+	if (req)
+		{
+		r=req->iClientRequest->SetStatus(aStatus);
+		req->iOwningThread=aThread;
+		((TClientDataRequest<TInt>*)req->iClientRequest)->SetDestPtr((TInt*)aLengthPtr);
+		// Add the request to the queue
+		iReqQueue->Add(req);		
+		}
+	else
+		r=KErrGeneral;				// Must have exceeded KMaxSndScRequestsPending.
+	NKern::FMSignal(&iMutex); 		// Release the buffer/request list mutex.
+	
+	return(r);
+	}
+
+/**
+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.
+		KErrNotReady if the channel is not configured (either for audio or its buffer config).
+*/
+TInt DSoundScLdd::ReleaseBuffer(TInt aChunkOffset)	
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::ReleaseBuffer(%x)",aChunkOffset));
+
+	TInt r=KErrNotReady;
+	if (iState!=EOpen && iBufManager)
+		{
+		TAudioBuffer* buf=NULL;
+		NKern::FMWait(&iMutex); 		// Acquire the buffer/request list mutex.
+		buf=((DRecordBufferManager*)iBufManager)->ReleaseBuffer(aChunkOffset);
+		NKern::FMSignal(&iMutex); 		// Release the buffer/request list mutex.	
+		if (buf)
+			{
+			buf->Flush(DBufferManager::EFlushBeforeDmaRead);
+			r=KErrNone;
+			}
+		else
+			r=KErrNotFound;
+		}
+	return(r);
+	}
+	
+/**
+Handle a custom configuration request.
+@param aFunction A number identifying the request.
+@param aParam A 32-bit value passed to the driver. Its meaning depends on the request.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/	
+TInt DSoundScLdd::CustomConfig(TInt aFunction,TAny* aParam)
+	{
+	
+	TInt r;
+	if (aFunction>KSndCustomConfigMaxReserved)
+		r=Pdd()->CustomConfig(aFunction,aParam);
+	else
+		{
+		r=KErrNotSupported;
+#ifdef _DEBUG		
+		switch (aFunction)
+			{
+			case KSndCustom_ForceHwConfigNotifSupported:
+				iCaps.iHwConfigNotificationSupport=ETrue;
+				r=KErrNone;
+				break;
+			case KSndCustom_CompleteChangeOfHwConfig:
+				NotifyChangeOfHwConfigCallback((TBool)aParam);
+				r=KErrNone;
+				break;
+			case KSndCustom_ForceStartTransferError:
+				iTestSettings|=KSoundScTest_StartTransferError;
+				r=KErrNone;
+				break;
+			case KSndCustom_ForceTransferDataError:
+				iTestSettings|=KSoundScTest_TransferDataError;
+				r=KErrNone;
+				break;
+			case KSndCustom_ForceTransferTimeout:
+				iTestSettings|=KSoundScTest_TransferTimeout;
+				r=KErrNone;
+				break;
+			}
+#endif	
+		}
+	return(r);	
+	}
+	
+/**
+@publishedPartner
+@prototype
+ 
+Called from the PDD each time it has completed a data transfer into the current record buffer. 
+The function performed here is to check whether the transfer into the current buffer is now complete. Also to queue
+further requests on the PDD which should now have the capability to accept more transfers. If transfer into the
+current buffer is now complete then we need to update the buffer lists and possibly complete a request back the client.
+While recording hasn't been paused and no error has occured then this completed buffer ought to be full. However, when
+recording has just been paused, the PDD can also call this function to complete a partially filled record buffer. In fact
+in some circumstances, pausing may result in the PDD calling this function where it turns out that no data has been
+recorded into this buffer. In this case we don't want to signal a null transfer back to the client.
+@param aTransferID A value provided by the LDD when it initiated the transfer allowing the transfer fragment to be 
+	uniquely identified.
+@param aTransferResult The result of the transfer being completed: KErrNone if successful, otherwise one of the other
+	system wide error codes.
+@param aBytesRecorded The number of bytes recorded into the record buffer.	
+*/
+void DSoundScLdd::RecordCallback(TUint aTransferID,TInt aTransferResult,TInt aBytesRecorded)
+	{
+#ifdef _DEBUG
+#ifdef TEST_WITH_PAGING_CACHE_FLUSHES
+	Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
+#endif
+#endif
+
+#ifdef _DEBUG	
+	// Test settings - only possible in debug mode.
+	if (iTestSettings & KSoundScTest_TransferDataError)
+		{
+		iTestSettings&=(~KSoundScTest_TransferDataError);
+		aTransferResult=KErrTimedOut;
+		}
+#endif
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::RecordCallback(ID:%xH,Len:%d) - %d",aTransferID,aBytesRecorded,aTransferResult));
+	
+	// If the transfer fragment is not for the current record buffer and were not paused then ignore it. Either the PDD
+	// has got very confused or more likely its a trailing fragment from an earlier buffer we have already failed. If 
+	// we're paused, the PDD doesn't need to bother with a transfer ID, we assume its for the current buffer.
+	if (aTransferID==iCurrentRecBufTf.iId || iState==EPaused)
+		{
+		// Update the count of bytes recorded.
+		iBytesTransferred+=aBytesRecorded;
+		
+		// Update the transfer status of the current buffer.
+		if (aTransferResult!=KErrNone)
+			{
+			// Transfer failed. Mark the buffer as being complete.
+			iCurrentRecBufTf.iTfState=TSndScTransfer::ETfDone;	
+			}
+		else	
+			iCurrentRecBufTf.SetCompleted(aBytesRecorded); // Transfer successful so update the progress.
+		
+		// Check if this is the PDD completing a fragment due to record being paused. In this situation we only allow the
+		// PDD to complete one fragment.	
+		TAudioBuffer* buf;
+		if (iState==EPaused && ++iCompletesWhilePausedCount<2)
+			{
+			// Complete (i.e. abort) the transfer to the current buffer.
+			iCurrentRecBufTf.iTfState=TSndScTransfer::ETfDone; 
+			
+			// Reset the transfer status for the pending record buffer. This will be switched to the current buffer later
+			// in this function - ready for when record is resumed.
+			buf=((DRecordBufferManager*)iBufManager)->GetNextRecordBuffer();
+			iNextRecBufTf.Init((TUint)buf,buf->iChunkOffset,buf->iSize,buf);		// Use pointer to record buffer as unique ID
+			}	
+		
+		// Check if we have just completed the transfer into the current buffer.	
+		if (iCurrentRecBufTf.iTfState==TSndScTransfer::ETfDone)
+			HandleCurrentRecordBufferDone(aTransferResult);
+		}
+			
+	// If we're not paused then the PDD should now have the capacity to accept another transfer so queue as many
+	// transfers on it as it can accept.
+	if (iState==EActive)
+		{
+#ifdef _DEBUG	
+		// Test settings - only possible in debug mode. Test LDD being slow servicing transfer completes from PDD. Disabled.
+/*		if (iTestSettings & KSoundScTest_TransferTimeout)
+			{
+			iTestSettings&=(~KSoundScTest_TransferTimeout);
+			Kern::NanoWait(500000000); // Pause for 0.5 second
+			} */
+#endif
+		TInt r=StartNextRecordTransfers();
+		if (r!=KErrNone)
+			{
+			// Problem starting the next transfer. That's fairly serious so complete all pending record requests and 
+			// stop recording.
+			Pdd()->StopTransfer();
+			iReqQueue->CompleteAll(r,&iMutex);
+			iState=EConfigured;
+			}
+		}
+	return;
+	}
+	
+/** Perform the necessary processing required when transfer into the current buffer is complete. This involves updating
+the buffer lists and possibly complete a request back the client.
+@param aTransferResult The result of the transfer being completed: KErrNone if successful, otherwise one of the other
+	system wide error codes.
+*/
+void DSoundScLdd::HandleCurrentRecordBufferDone(TInt aTransferResult)
+	{
+	TAudioBuffer* buf;
+	
+	// Flush the buffer before acquiring the mutex.
+	buf=((DRecordBufferManager*)iBufManager)->GetCurrentRecordBuffer();
+	buf->Flush(DBufferManager::EFlushAfterDmaRead);
+	
+	NKern::FMWait(&iMutex); 		// Acquire the buffer/request list mutex.
+	
+	// Update the buffer list (by either adding the current buffer to the completed list or the free list).
+	TInt bytesRecorded=iCurrentRecBufTf.GetLengthTransferred();
+	((DRecordBufferManager*)iBufManager)->SetBufferFilled(bytesRecorded,aTransferResult);
+    
+    // The pending buffer now becomes the current one and we need to get a new pending one.
+    iCurrentRecBufTf=iNextRecBufTf;
+    buf=((DRecordBufferManager*)iBufManager)->GetNextRecordBuffer();
+    iNextRecBufTf.Init((TUint)buf,buf->iChunkOffset,buf->iSize,buf);	// Use pointer to record buffer as unique ID
+    
+    // Check if there is a client record request pending.
+    if (!iReqQueue->IsEmpty())
+    	{
+    	// A record request is pending. Check if we have had an overflow since the last record request was completed.
+    	if (((DRecordBufferManager*)iBufManager)->iBufOverflow)
+    		{
+    		TSoundScRequest* req=iReqQueue->Remove();
+    		DThread* thread=req->iOwningThread;					// Take a copy before we free it.
+			TClientRequest* clreq = req->iClientRequest;		// Take a copy before we free it.
+    		((DRecordBufferManager*)iBufManager)->iBufOverflow=EFalse;
+			NKern::FMSignal(&iMutex); 							// Release the buffer/request list mutex.
+			iReqQueue->Free(req);
+    		CompleteRequest(thread,NULL,KErrOverflow,clreq);			// Complete the request.
+    		}
+    	else
+    		{
+	    	// Check there really is a buffer available. (There's no guarentee the one just completed hasn't
+	    	// immediately been queued again: if the client has too many 'in-use' or the one completed was a NULL
+	    	// transfer due to pausing).
+			TAudioBuffer* buf=((DRecordBufferManager*)iBufManager)->GetBufferForClient();
+			if (buf)
+				{
+				// There still a buffer available so complete the request.
+				TSoundScRequest* req=iReqQueue->Remove();
+				DThread* thread=req->iOwningThread;							// Take a copy before we free it.
+				TClientRequest* clreq = req->iClientRequest;				// Take a copy before we free it.
+				NKern::FMSignal(&iMutex); 	// Release the buffer/request list mutex.
+				iReqQueue->Free(req);
+				if (buf->iResult==KErrNone)
+					{
+					((TClientDataRequest<TInt>*)clreq)->Data() = buf->iBytesAdded;
+					CompleteRequest(thread,NULL,buf->iChunkOffset,clreq);					// Complete the request.	
+					}
+				else	
+					CompleteRequest(thread,NULL,buf->iResult,clreq);				// Complete the request.
+				}
+			else
+				NKern::FMSignal(&iMutex); 	// Release the buffer/request list mutex.	
+    		}
+    	}
+    else
+    	NKern::FMSignal(&iMutex); 	// Release the buffer/request list mutex.	
+	}
+	
+/**
+This function starts the next record data transfer. It starts with the current record buffer - checking whether all of
+this has now been transferred or queued for transfer. If not it breaks this down into transfers sizes which are
+compatible with the PDD and then repeatedly attempts to queue these on the PDD until the PDF indicates that it can
+accept no more transfers for the moment. If the record buffer is fully started in this way and the PDD still has the
+capacity to accept more transfers then it moves on to start the pending record buffer.
+@return Normally KErrNone unless the PDD incurs an error while attempting to start a new transfer.
+*/
+TInt DSoundScLdd::StartNextRecordTransfers()
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::StartNextRecordTransfers"));
+	
+	// First start with the current record buffer - keep queuing transfers either until this buffer is
+	// fully started or until the PDD can accept no more transfers.
+	TInt r=KErrNone;
+	while (r==KErrNone && iCurrentRecBufTf.iTfState<TSndScTransfer::ETfFullyStarted)
+		{
+		TInt pos=iCurrentRecBufTf.GetStartOffset();
+		TPhysAddr physAddr;
+		TInt len=iCurrentRecBufTf.iAudioBuffer->GetFragmentLength(pos,iCurrentRecBufTf.GetNotStartedLen(),physAddr);
+		
+		r=Pdd()->TransferData(iCurrentRecBufTf.iId,(iBufManager->iChunkBase+pos),physAddr,len);
+		__KTRACE_OPT(KSOUND1, Kern::Printf("<PDD:TransferData(off:%x len:%d) - %d",pos,len,r));
+		if (r==KErrNone)
+			iCurrentRecBufTf.SetStarted(len);	// Successfully queued a transfer - update the status.
+		}
+	
+	// Either the current record transfer is now fully started, or the PDD can accept no more transfers
+	// If the PDD can still accept more transfers then move on to the next record buffer - again, keep queuing
+	// transfers either until this buffer is fully started or until the PDD can accept no more.
+	while (r==KErrNone && iNextRecBufTf.iTfState<TSndScTransfer::ETfFullyStarted)
+		{
+		TInt pos=iNextRecBufTf.GetStartOffset();
+		TPhysAddr physAddr;
+		TInt len=iNextRecBufTf.iAudioBuffer->GetFragmentLength(pos,iNextRecBufTf.GetNotStartedLen(),physAddr);
+		
+		r=Pdd()->TransferData(iNextRecBufTf.iId,(iBufManager->iChunkBase+pos),physAddr,len);
+		__KTRACE_OPT(KSOUND1, Kern::Printf("<PDD:TransferData(off:%x len:%d) - %d",pos,len,r));
+		if (r==KErrNone)
+			iNextRecBufTf.SetStarted(len);	// Successfully queued a transfer - update the status.
+		}
+	if (r==KErrNotReady)
+		r=KErrNone;		// KErrNotReady means the PDD the cannot accept any more requests - this isn't an error.	
+	return(r);	
+	}	
+	
+/**
+@publishedPartner
+@prototype
+
+Called from the PDD each time it detects a change in the hardware configuration of the device.
+@param aHeadsetPresent This is set by the PDD to ETrue if a microphone or headset socket is now present or EFalse if 
+such a device is not present.
+*/
+void DSoundScLdd::NotifyChangeOfHwConfigCallback(TBool aHeadsetPresent)
+	{
+#ifdef _DEBUG
+#ifdef TEST_WITH_PAGING_CACHE_FLUSHES 
+	Kern::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
+#endif
+#endif
+
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::NotifyChangeOfHwConfigCallback(Pres:%d)",aHeadsetPresent));
+	
+	__ASSERT_DEBUG(iCaps.iHwConfigNotificationSupport,Kern::Fault(KSoundLddPanic,__LINE__));		
+	
+	if (iNotifyChangeOfHwClientRequest->IsReady())
+		{
+		iNotifyChangeOfHwClientRequest->Data() = aHeadsetPresent;
+		CompleteRequest(iChangeOfHwConfigThread,NULL,KErrNone,iNotifyChangeOfHwClientRequest);	// Complete the request.
+		}
+	}
+	
+/**
+This function validates that a new sound format configuration is both sensible and supported by this device.
+@param aConfig A reference to the new sound format configuration object.
+*/	
+TInt DSoundScLdd::ValidateConfig(const TCurrentSoundFormatV02& aConfig)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScLdd::ValidateConfig"));
+	
+	// Check that the audio channel configuration requested is sensible and supported by this device.
+	if (aConfig.iChannels<0) 
+		return(KErrNotSupported);
+	TInt chans=(aConfig.iChannels-1);
+	if (!(iCaps.iChannels & (1<<chans)))
+		return(KErrNotSupported);
+	
+	// Check that the sample rate requested is sensible and supported by this device.
+	if (aConfig.iRate<0 || !(iCaps.iRates & (1<<aConfig.iRate)))
+		return(KErrNotSupported);
+	
+	// Check that the encoding format requested is sensible and supported by this device.
+	if (aConfig.iEncoding<0 || !(iCaps.iEncodings & (1<<aConfig.iEncoding)))
+		return(KErrNotSupported);
+	
+	// Check that the data format requested is sensible and supported by this device.
+	if (aConfig.iDataFormat<0 || !(iCaps.iDataFormats & (1<<aConfig.iDataFormat)))
+		return(KErrNotSupported);
+		
+	__KTRACE_OPT(KSOUND1, Kern::Printf("<DSoundScLdd::ValidateConfig - %d",KErrNone));
+	return(KErrNone);
+	}
+	
+/**
+Increase or decrease the memory area allocated to hold the current buffer configuration in the play/record chunk.
+@param aNumBuffers The number of buffers within the new buffer configuration. This determines the size of the memory
+	area required.
+@pre The thread must be in a critical section. 
+*/
+TInt DSoundScLdd::ReAllocBufferConfigInfo(TInt aNumBuffers)
+	{
+	if (iBufConfig)
+		{
+		delete iBufConfig;
+		iBufConfig=NULL;
+		}	 
+	
+	iBufConfigSize=aNumBuffers*sizeof(TInt);	
+	iBufConfigSize+=sizeof(TSharedChunkBufConfigBase);
+	iBufConfig=(TSoundSharedChunkBufConfig*)Kern::AllocZ(iBufConfigSize);
+	if (!iBufConfig)
+		return(KErrNoMemory);	
+	
+	return(KErrNone);
+	}
+
+/**
+Start the EOF play timer. A 2 second timer is queued each time the transfer of playback data ceases by the LLD and if it
+is allowed to expire, then the PDD is called to release any resources in use for playback transfer. 
+*/
+void DSoundScLdd::StartPlayEofTimer()
+	{
+	iEofTimer.Cancel();
+	iPlayEofDfc.Cancel();
+	iEofTimer.OneShot(NKern::TimerTicks(2000)); // Queue the 2 second EOF timer to stop transfer on the PDD.
+	iPlayEofTimerActive=ETrue;
+	}
+
+/**
+Cancel the EOF play timer. 
+*/	
+void DSoundScLdd::CancelPlayEofTimer()
+	{
+	iEofTimer.Cancel();
+	iPlayEofDfc.Cancel();
+	iPlayEofTimerActive=EFalse;
+	}
+	
+/**
+@publishedPartner
+@prototype
+
+Returns the buffer configuration of the play/record chunk.
+@return A pointer to the current buffer configuration of the play/record chunk.
+*/	
+TSoundSharedChunkBufConfig* DSoundScLdd::BufConfig()
+	{
+	return(iBufConfig);
+	}
+
+/**
+@publishedPartner
+@prototype
+
+Returns the address of the start of the play/record chunk.
+@return The linear address of the start of the play/record chunk.
+*/	
+TLinAddr DSoundScLdd::ChunkBase()
+	{
+	return(iBufManager->iChunkBase);
+	}			
+		
+/**
+The ISR to handle the EOF play timer.
+@param aChannel A pointer to the sound driver logical channel object.
+*/	
+void DSoundScLdd::PlayEofTimerExpired(TAny* aChannel)
+	{
+	DSoundScLdd& drv=*(DSoundScLdd*)aChannel;
+	
+	drv.iPlayEofDfc.Add();
+	}
+	
+/**
+The DFC used to handle the EOF play timer. 
+@param aChannel A pointer to the sound driver logical channel object.
+*/	
+void DSoundScLdd::PlayEofTimerDfc(TAny* aChannel)
+	{	
+	DSoundScLdd& drv=*(DSoundScLdd*)aChannel;
+	
+	drv.Pdd()->StopTransfer();
+	drv.iState=EConfigured;
+	drv.iPlayEofTimerActive=EFalse;
+	}				
+	
+/**
+The DFC used to handle power down requests from the power manager before a transition into system
+shutdown/standby.
+@param aChannel A pointer to the sound driver logical channel object.
+*/
+void DSoundScLdd::PowerDownDfc(TAny* aChannel)
+	{
+	DSoundScLdd& drv=*(DSoundScLdd*)aChannel;
+	drv.Shutdown();
+	drv.iPowerHandler->PowerDownDone();
+	}
+	
+/**
+The DFC used to handle power up requests from the power manager following a transition out of system standby.
+@param aChannel A pointer to the sound driver logical channel object.
+*/	
+void DSoundScLdd::PowerUpDfc(TAny* aChannel)
+	{
+	DSoundScLdd& drv=*(DSoundScLdd*)aChannel;
+	
+	// Restore the channel to a default state.
+	drv.DoCancel(RSoundSc::EAllRequests);
+	drv.Pdd()->PowerUp();
+	drv.DoSetSoundConfig(drv.iSoundConfig);
+	drv.SetVolume(drv.iVolume);			
+	drv.iState=(!drv.iBufConfig)?EOpen:EConfigured;
+	
+	drv.iPowerHandler->PowerUpDone();
+	}	
+
+/** 
+Complete an asynchronous request back to the client.
+@param aThread The client thread which issued the request.
+@param aStatus The TRequestStatus instance that will receive the request status code or NULL if aClientRequest used. 
+@param aReason The request status code.  
+@param aClientRequest The TClientRequest instance that will receive the request status code or NULL if aStatus used. 
+@pre The thread must be in a critical section. 
+*/
+
+void DSoundScLdd::CompleteRequest(DThread* aThread, TRequestStatus* aStatus, TInt aReason, TClientRequest* aClientRequest)
+	{
+	if (aClientRequest)
+		{
+		if (aClientRequest->IsReady())
+			{
+			Kern::QueueRequestComplete(aThread,aClientRequest,aReason);
+			}
+		else
+			{
+			// should always be ready
+			__ASSERT_DEBUG(EFalse,Kern::Fault(KSoundLddPanic,__LINE__));
+			}
+		}
+	else if (aStatus)
+		{
+		Kern::RequestComplete(aStatus,aReason);		// Complete the request back to the client.
+		}
+	else
+		{
+		// never get here - either aStatus or aClientRequest must be valid
+		__ASSERT_DEBUG(EFalse,Kern::Fault(KSoundLddPanic,__LINE__));
+		}
+	
+	aThread->AsyncClose();	// Asynchronously close our reference on the client thread - don't want to be blocked if this is final reference. 
+	
+#ifdef _DEBUG	
+	__e32_atomic_add_ord32(&iThreadOpenCount, TUint32(-1));
+#endif		
+	}
+
+/**
+Constructor for the play request object.
+*/
+TSoundScPlayRequest::TSoundScPlayRequest()
+	: TSoundScRequest()
+	{
+	iFlags=0; 
+	iCompletionReason=KErrGeneral;
+	}
+
+/*
+Second phase construction of the requests
+*/
+TInt TSoundScPlayRequest::Construct()
+	{
+	return Kern::CreateClientRequest(iClientRequest);
+	}
+
+/*
+Second phase construction of the requests
+*/
+TInt TSoundScRequest::Construct()
+	{
+	TClientDataRequest<TInt>* tempClientDataRequest=0;
+	TInt r = Kern::CreateClientDataRequest(tempClientDataRequest);
+	iClientRequest = tempClientDataRequest;
+	return r;
+	}
+
+/**
+Destructor of play requests
+*/
+TSoundScRequest::~TSoundScRequest()
+	{
+	Kern::DestroyClientRequest(iClientRequest);
+	}
+
+/**
+Constructor for the request object queue.
+*/
+TSoundScRequestQueue::TSoundScRequestQueue(DSoundScLdd* aLdd)
+	{
+	iLdd=aLdd;
+	memclr(&iRequest[0],sizeof(TSoundScRequest*)*KMaxSndScRequestsPending);
+	}
+	
+/**
+Destructor for the request object queue.
+*/
+TSoundScRequestQueue::~TSoundScRequestQueue()
+	{
+	for (TInt i=0 ; i<KMaxSndScRequestsPending ; i++)
+		{
+		delete iRequest[i];
+		}
+	}
+	
+/**
+Second stage constructor for the basic request object queue.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+@pre The thread must be in a critical section.
+*/
+TInt TSoundScRequestQueue::Create()	
+	{
+	// Create the set of available request objects and add them to the unused request queue. 	
+	for (TInt i=0 ; i<KMaxSndScRequestsPending ; i++)
+		{
+		iRequest[i]=new TSoundScRequest;		// Normal request object
+		if (!iRequest[i])
+			return(KErrNoMemory);
+		TInt retConstruct = iRequest[i]->Construct();
+		if ( retConstruct != KErrNone)
+			{
+			return(retConstruct);
+			}
+		iUnusedRequestQ.Add(iRequest[i]);
+		}
+		
+	return(KErrNone);
+	}
+		
+/**
+Second stage constructor for the play request object queue.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+@pre The thread must be in a critical section.
+*/
+TInt TSoundScPlayRequestQueue::Create()	
+	{
+	// Create the set of available play request objects and add them to the unused request queue. 	
+	for (TInt i=0 ; i<KMaxSndScRequestsPending ; i++)
+		{
+		iRequest[i]=new TSoundScPlayRequest();
+		if (!iRequest[i])
+			return(KErrNoMemory);
+		TInt retConstruct = iRequest[i]->Construct();
+		if ( retConstruct != KErrNone)
+			{
+			return(retConstruct);
+			}
+		iUnusedRequestQ.Add(iRequest[i]);
+		}
+		
+	return(KErrNone);
+	}	
+
+/**
+Get an unused request object.
+@return A pointer to a free request object or NULL if there are none available.
+*/ 	
+TSoundScRequest* TSoundScRequestQueue::NextFree()
+	{
+	NKern::FMWait(&iUnusedRequestQLock);
+	TSoundScRequest* req = (TSoundScRequest*)iUnusedRequestQ.GetFirst();
+	NKern::FMSignal(&iUnusedRequestQLock);
+	return req;
+	}
+		
+/**
+Add a request object to the tail of the pending request queue. 
+@param aReq A pointer to the request object to be added to the queue.
+*/ 
+void TSoundScRequestQueue::Add(TSoundScRequest* aReq)	
+	{
+	iPendRequestQ.Add(aReq);
+	} 	
+
+/**
+If the pending request queue is not empty, remove the request object from the head of this queue.
+@return A pointer to request object removed or NULL if the list was empty.
+*/ 
+TSoundScRequest* TSoundScRequestQueue::Remove()
+	{
+	return((TSoundScRequest*)iPendRequestQ.GetFirst());
+	}
+						
+/**
+Remove a request object from anywhere in the pending request queue. 
+@param aReq A pointer to the request object to be removed from the queue.
+@return A pointer to request object removed or NULL if it wasn't found.
+*/	
+TSoundScRequest* TSoundScRequestQueue::Remove(TSoundScRequest* aReq)	
+	{
+	TSoundScRequest* retReq;
+	
+	// Scan through the pending queue looking for a request object which matches.
+	retReq=(TSoundScRequest*)iPendRequestQ.First();
+	while (!IsAnchor(retReq) && retReq!=aReq)
+		retReq=(TSoundScRequest*)retReq->iNext;
+	
+	// If we got a match then remove the request object from the queue and return it.
+	if (!IsAnchor(retReq))
+		retReq->Deque();
+	else
+		retReq=NULL;
+	return(retReq);
+	}
+
+/**
+Free up a request object - making it available for further requests. 
+@param aReq A pointer to the request object being freed up.
+*/ 
+void TSoundScRequestQueue::Free(TSoundScRequest* aReq)	
+	{
+	NKern::FMWait(&iUnusedRequestQLock);
+	iUnusedRequestQ.Add(aReq);
+	NKern::FMSignal(&iUnusedRequestQLock);
+	}
+	 
+/**
+Find a request object (specified by its associated request status pointer) within in the pending request queue. 
+@param aStatus The request status pointer of the request object to be found in the queue.
+@return A pointer to the request object if it was found or NULL if it wasn't found.
+*/	
+TSoundScRequest* TSoundScRequestQueue::Find(TRequestStatus* aStatus)	
+	{
+	TSoundScRequest* retReq;
+	
+	// Scan through the queue looking for a request object containing a TRequestStatus* which matches.
+	retReq=(TSoundScRequest*)iPendRequestQ.First();
+	while (!IsAnchor(retReq) && retReq->iClientRequest->StatusPtr()!=aStatus)
+		retReq=(TSoundScRequest*)retReq->iNext;
+		
+	return((IsAnchor(retReq))?NULL:retReq);
+	}				
+	
+/**
+Remove each request object from the pending request queue, completing each request removed with a specified completion
+reason.
+@param aCompletionReason The error value to be returned when completing any requests in the queue.
+@param aMutex A pointer to a mutex to be aquired when removing requests from the queue. May be NULL.
+*/		
+void TSoundScRequestQueue::CompleteAll(TInt aCompletionReason,NFastMutex* aMutex)	
+	{
+	if (aMutex)
+		NKern::FMWait(aMutex); 							// Acquire the mutex.
+	
+	TSoundScRequest* req;
+	while ((req=Remove())!=NULL)
+		{
+		if (aMutex)
+			NKern::FMSignal(aMutex); 					// Release the mutex while we complete the request.
+		iLdd->CompleteRequest(req->iOwningThread,NULL,aCompletionReason,req->iClientRequest);	
+		Free(req);	
+		if (aMutex)
+			NKern::FMWait(aMutex); 						// Re-acquire the mutex.
+
+		}
+		
+	if (aMutex)	
+		NKern::FMSignal(aMutex); 						// Release mutex.	
+	}
+
+/**
+Constructor for the play request object queue.
+*/					
+TSoundScPlayRequestQueue::TSoundScPlayRequestQueue(DSoundScLdd* aLdd)
+	: TSoundScRequestQueue(aLdd)
+{
+}
+
+/**
+Return the play request object from the request queue which is next to be transferrred. If this
+play request is being handled using multiple data transfers then the transfer of earlier parts of
+this request may already be in progress. 
+@return Either a pointer to the next play request object for transfer, or NULL if no more are pending. 	
+*/
+TSoundScPlayRequest* TSoundScPlayRequestQueue::NextRequestForTransfer()
+	{
+	TSoundScPlayRequest* retReq;
+	
+	retReq=(TSoundScPlayRequest*)iPendRequestQ.First();
+	while (!IsAnchor(retReq) && retReq->iTf.iTfState>TSndScTransfer::ETfPartlyStarted)
+		retReq=(TSoundScPlayRequest*)retReq->iNext;
+		
+	return((IsAnchor(retReq))?NULL:retReq);	
+	}	
+
+/**
+Search the play request queue for a particular play request object specified by its transfer ID.
+@param aTransferID The transfer ID of the particular play request object to be found.
+@param aIsNextToComplete If the search is successful then this indicates whether the request 
+object found is the next in the queue to be completed to the client. ETrue if next to be 
+completed, EFalse otherwise.
+@return Either a pointer to the specified request object, or NULL if it was not found.
+*/	
+TSoundScPlayRequest* TSoundScPlayRequestQueue::Find(TUint aTransferID,TBool& aIsNextToComplete)
+	{
+	TSoundScPlayRequest* retReq;
+	TSoundScPlayRequest* nextToCompleteReq=NULL;
+	
+	retReq=(TSoundScPlayRequest*)iPendRequestQ.First();
+	
+	// Walk all the way through the list either until we find the specified object or until we get to the end
+	for ( ; !IsAnchor(retReq) ; retReq=(TSoundScPlayRequest*)retReq->iNext )
+		{
+		// The first request we find which isn't complete must be the next to complete
+		if (!nextToCompleteReq && retReq->iTf.iTfState!=TSndScTransfer::ETfDone)
+			nextToCompleteReq=retReq;
+		if (retReq->iTf.iId==aTransferID)
+			break;
+		}
+	
+	if (IsAnchor(retReq))
+		return(NULL);		// Object not found
+	else
+		{
+		aIsNextToComplete=(retReq==nextToCompleteReq);
+		return(retReq);		// Object found
+		}
+	}					
+/**
+Constructor for the audio data transfer class. 
+*/	
+TSndScTransfer::TSndScTransfer()
+	{
+	iId=0;
+	iTfState=ETfNotStarted;
+	iAudioBuffer=NULL;
+	iLengthTransferred=0;
+	iTransfersInProgress=0;
+	}
+	
+/**
+Initialisation function for the audio data transfer class.
+@param aId A value to uniquely identify this particular transfer.
+@param aChunkOffset The start postition of the transfer - an offset within the shared chunk.
+@param aLength The total length of the transfer in bytes.
+@param anAudioBuffer The audio buffer associated with the transfer.
+*/		
+void TSndScTransfer::Init(TUint aId,TInt aChunkOffset,TInt aLength,TAudioBuffer* anAudioBuffer)
+	{
+	iId=aId;
+	iTfState=ETfNotStarted;
+	iAudioBuffer=anAudioBuffer;
+	iStartedOffset=aChunkOffset;
+	iEndOffset=aChunkOffset+aLength;
+	iLengthTransferred=0;
+	iTransfersInProgress=0;
+	}
+	
+/**
+Update the progress of the audio data transfer with the amount of data now queued for transfer on the audio device.
+@param aLength The amount of data (in bytes) that has just been queued on the audio device.
+*/
+void TSndScTransfer::SetStarted(TInt aLength)
+	{	
+	iTransfersInProgress++;
+	iStartedOffset+=aLength;
+	TInt notqueued=(iEndOffset - iStartedOffset);
+	__ASSERT_ALWAYS(notqueued>=0,Kern::Fault(KSoundLddPanic,__LINE__));
+	iTfState=(notqueued) ? ETfPartlyStarted : ETfFullyStarted;
+	}
+	
+/**
+Update the progress of the audio data transfer with the amount of data now successfully transfered by the audio device.
+@param aLength The amount of data (in bytes) that has just been transferred by the audio device.
+@return ETrue if the transfer is now fully complete, otherwise EFalse.
+*/
+TBool TSndScTransfer::SetCompleted(TInt aLength)
+	{		
+	iLengthTransferred+=aLength;
+	iTransfersInProgress--;
+	__ASSERT_ALWAYS(iTransfersInProgress>=0,Kern::Fault(KSoundLddPanic,__LINE__));
+	
+	if (GetNotStartedLen()==0 && iTransfersInProgress==0)
+		{
+		iTfState=ETfDone;	// Transfer is now fully completed
+		return(ETrue);
+		}
+	else
+		return(EFalse);	
+	}
+		
+/**
+Constructor for the sound driver power handler class.
+@param aChannel A pointer to the sound driver logical channel which owns this power handler.
+*/
+DSoundScPowerHandler::DSoundScPowerHandler(DSoundScLdd* aChannel)
+:	DPowerHandler(KDevSoundScName),
+	iChannel(aChannel)
+	{	
+	}
+
+/**
+A request from the power manager for the power down of the audio device.
+This is called during a transition of the phone into standby or power off.
+@param aState The target power state; can be EPwStandby or EPwOff only.
+*/
+void DSoundScPowerHandler::PowerDown(TPowerState aPowerState)
+	{
+	(void)aPowerState;
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScPowerHandler::PowerDown(State-%d)",aPowerState));
+	
+	// Power-down involves hardware access so queue a DFC to perform this from the driver thread.
+	iChannel->iPowerDownDfc.Enque();
+	}
+
+/**
+A request from the power manager for the power up of the audio device.
+This is called during a transition of the phone out of standby.
+*/
+void DSoundScPowerHandler::PowerUp()
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DSoundScPowerHandler::PowerUp"));
+	
+	// Power-up involves hardware access so queue a DFC to perform this from the driver thread.
+	iChannel->iPowerUpDfc.Enque();
+	}	
+
+/**
+Constructor for the buffer manager.
+*/
+DBufferManager::DBufferManager(DSoundScLdd* aLdd)
+	: iLdd(aLdd)
+	{
+//	iChunk=NULL;
+//	iNumBuffers=0;
+//	iAudioBuffers=NULL;	
+//	iMaxTransferLen=0;	
+	}
+
+/**
+Destructor for the buffer manager.
+@pre The thread must be in a critical section.
+*/
+DBufferManager::~DBufferManager()
+	{
+	if (iChunk)
+		Kern::ChunkClose(iChunk);
+	delete[] iAudioBuffers;
+	}
+		
+/**
+Second stage constructor for the buffer manager. This version creates a shared chunk and a buffer object for each
+buffer specified within this. Then it commits memory within the chunk for each of these buffers. This also involves the
+creation of a set of buffer lists to manage the buffers.
+@param aBufConfig The shared chunk buffer configuration object specifying the geometry of the buffer configuration
+required.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+@pre The thread must be in a critical section.
+*/
+TInt DBufferManager::Create(TSoundSharedChunkBufConfig* aBufConfig)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::Create(Bufs-%d,Sz-%d)",aBufConfig->iNumBuffers,aBufConfig->iBufferSizeInBytes));
+
+	// Create the required number of buffer objects, and the buffer lists to manage these.
+	TInt r=CreateBufferLists(aBufConfig->iNumBuffers);
+	if (r!=KErrNone)
+		return(r);
+		
+	TInt chunkSz;
+	TInt bufferSz=aBufConfig->iBufferSizeInBytes;
+	TInt* bufferOffsetList=&aBufConfig->iBufferOffsetListStart;	
+	
+	// Calculate the required size for the chunk and the buffer offsets. 
+	if (aBufConfig->iFlags & KScFlagUseGuardPages)
+		{
+		// Commit each buffer separately with an uncommitted guard pages around each buffer.
+		TInt guardPageSize=Kern::RoundToPageSize(1);
+		bufferSz=Kern::RoundToPageSize(aBufConfig->iBufferSizeInBytes); // Commit size to be a multiple of the MMU page size.
+		chunkSz=guardPageSize;											// Leave an un-committed guard page at the start.
+		for (TInt i=0 ; i<aBufConfig->iNumBuffers ; i++)
+			{
+			bufferOffsetList[i]=chunkSz;
+			chunkSz += (bufferSz + guardPageSize);						// Leave an un-committed guard page after each buffer.
+			}
+		}
+	else
+		{
+		// Commit all the buffers contiguously into a single region (ie with no guard pages between each buffer).
+		chunkSz=0;
+		for (TInt i=0 ; i<aBufConfig->iNumBuffers ; i++)
+			{
+			bufferOffsetList[i]=chunkSz;
+			chunkSz += bufferSz;
+			}
+		chunkSz=Kern::RoundToPageSize(chunkSz);							// Commit size to be a multiple of the MMU page size.
+		}	
+	aBufConfig->iFlags|=KScFlagBufOffsetListInUse;
+	__KTRACE_OPT(KSOUND1, Kern::Printf("Chunk size is %d bytes",chunkSz));	
+    
+    // Create the shared chunk. The PDD supplies most of the chunk create info - but not the maximum size.
+	TChunkCreateInfo info;
+	info.iMaxSize=chunkSz;
+	iLdd->Pdd()->GetChunkCreateInfo(info);		// Call down to the PDD for the rest.
+	
+	r = Kern::ChunkCreate(info,iChunk,iChunkBase,iChunkMapAttr);
+	__KTRACE_OPT(KSOUND1, Kern::Printf("Create chunk - %d",r));	
+	if (r!=KErrNone)
+     	return(r);
+	
+	if (aBufConfig->iFlags & KScFlagUseGuardPages)
+		{
+		// Map each of the buffers into the chunk separately - try to allocate physically contiguous RAM pages. Create a buffer object for each buffer.
+ 		TBool isContiguous;
+ 		for (TInt i=0 ; i<aBufConfig->iNumBuffers ; i++)
+			{
+			r=CommitMemoryForBuffer(bufferOffsetList[i],bufferSz,isContiguous);
+			if (r!=KErrNone)
+				return(r);
+			r=iAudioBuffers[i].Create(iChunk,bufferOffsetList[i],(aBufConfig->iBufferSizeInBytes),isContiguous,this);
+			if (r!=KErrNone)
+				return(r);	
+			}		
+		}
+	else
+		{
+		// Map memory for the all buffers into the chunk - try to allocate physically contiguous RAM pages. 
+ 		TBool isContiguous;
+		r=CommitMemoryForBuffer(0,chunkSz,isContiguous);
+		if (r!=KErrNone)
+			return(r);
+		
+		// Create a buffer object for each buffer.
+ 		for (TInt i=0 ; i<aBufConfig->iNumBuffers ; i++)
+			{
+			r=iAudioBuffers[i].Create(iChunk,bufferOffsetList[i],(aBufConfig->iBufferSizeInBytes),isContiguous,this);
+			if (r!=KErrNone)
+				return(r);	
+			}	
+		}
+
+	// Read back and store the maximum transfer length supported by this device from the PDD.
+	iMaxTransferLen=iLdd->Pdd()->MaxTransferLen();
+	
+    __KTRACE_OPT(KSOUND1, Kern::Printf("<DBufferManager::Create - %d",r));
+	return(r);
+	}
+	
+/**
+Second stage constructor for the buffer manager. This version opens an existing shared chunk using a client supplied
+handle. It then creates a buffer object for each buffer that exists within the chunk as well as creating a set of buffer
+lists to manage the buffers.
+@param aBufConfig The shared chunk buffer configuration object - specifying the geometry of the buffer configuration
+within the shared chunk supplied.
+@param aChunkHandle A handle for the shared chunk supplied by the client.
+@param anOwningThread The thread in which the given handle is valid.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+@pre The thread must be in a critical section.
+*/
+TInt DBufferManager::Create(TSoundSharedChunkBufConfig& aBufConfig,TInt aChunkHandle,DThread* anOwningThread)
+	{	
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::Create(Handle-%d)",aChunkHandle));
+
+	// Validate the buffer configuration information.
+	if (!aBufConfig.iFlags&KScFlagBufOffsetListInUse)
+		return(KErrArgument);
+	TInt numBuffers=aBufConfig.iNumBuffers;
+	TInt bufferSizeInBytes=aBufConfig.iBufferSizeInBytes;
+	TInt* bufferOffsetList=&aBufConfig.iBufferOffsetListStart;
+	TInt r=ValidateBufferOffsets(bufferOffsetList,numBuffers,bufferSizeInBytes);
+	if (r<0)
+		return(r);
+	
+	// Create the required number of buffer objects, and the buffer lists to manage these.
+	r=CreateBufferLists(numBuffers);
+	if (r!=KErrNone)
+		return(r);
+	
+	// Open the shared chunk.
+	DChunk* chunk;
+	chunk=Kern::OpenSharedChunk(anOwningThread,aChunkHandle,ETrue);
+	if (!chunk)
+		return(KErrBadHandle);
+	iChunk=chunk;
+	
+	// Read the physical address for the 1st buffer in order to determine the kernel address and the map attributes.
+	TInt offset=bufferOffsetList[0];
+	TPhysAddr physAddr;
+	TLinAddr kernelAddress;
+	r=Kern::ChunkPhysicalAddress(iChunk,offset,bufferSizeInBytes,kernelAddress,iChunkMapAttr,physAddr,NULL);
+	if (r!=KErrNone)
+		return(r);
+	iChunkBase=(kernelAddress-offset);
+	
+	// For each buffer, validate that the buffer specified contains committed memory and store the buffer info. into each buffer object.
+	while (numBuffers)
+		{
+		numBuffers--;
+		offset=bufferOffsetList[numBuffers];
+		// Assume it isn't contiguous here - Create() will detect and do the right thing if it is contiguous.
+		r=iAudioBuffers[numBuffers].Create(iChunk,offset,bufferSizeInBytes,EFalse,this);	
+		if (r!=KErrNone)
+			return(r);
+		}
+		
+	// Read back and store the maximum transfer length supported by this device from the PDD.
+	iMaxTransferLen=iLdd->Pdd()->MaxTransferLen();	
+		
+	__KTRACE_OPT(KSOUND1, Kern::Printf("<DBufferManager::Create - %d",KErrNone));
+	return(KErrNone);
+	}
+
+/**
+Allocate an array of buffer objects, - one for each buffer contained within the shared chunk.
+@param aNumBuffers The number of buffer objects required.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+@pre The thread must be in a critical section.
+*/
+TInt DBufferManager::CreateBufferLists(TInt aNumBuffers)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::CreateBufferLists(Bufs-%d)",aNumBuffers));
+
+	// Construct the array of buffers.
+	iNumBuffers=aNumBuffers;
+	iAudioBuffers=new TAudioBuffer[aNumBuffers];
+	if (!iAudioBuffers)
+		return(KErrNoMemory);
+	
+	return(KErrNone);
+	}
+	
+/**
+Validate a shared chunk buffer offset list.
+@param aBufferOffsetList The buffer offset list to be validated.
+@param aNumBuffers The number of offsets that the list contains.
+@param aBufferSizeInBytes The size in bytes of each buffer.
+@return If the buffer list is found to be valid, the calculated minimum size of the corresponding chunk is returned
+	(i.e. a value>=0). Otherwise, KErrArgument is returned.
+*/
+TInt DBufferManager::ValidateBufferOffsets(TInt* aBufferOffsetList,TInt aNumBuffers,TInt aBufferSizeInBytes)	
+	{
+	TUint32 alignmask=(1<<iLdd->iCaps.iRequestAlignment)-1; // iRequestAlignment holds log to base 2 of alignment required
+			
+	// Verify each of the buffer offsets supplied
+	TInt offset=0;
+	for (TInt i=0 ; i<aNumBuffers ; i++)
+		{
+		// If this is a record channel then the offset must comply with the PDD alignment constraints.
+		if (iLdd->iDirection==ESoundDirRecord && ((TUint32)aBufferOffsetList[i] & alignmask) != 0)	
+			return(KErrArgument);
+			
+		// Check the offset doesn't overlap the previous buffer - offset holds the offset to next byte after
+		// the previous buffer.
+		if (aBufferOffsetList[i]<offset)
+			return(KErrArgument);
+		
+		offset=(aBufferOffsetList[i]+aBufferSizeInBytes);
+		}
+	return(offset);	
+	}
+	
+/**
+Verify that a specified region of the shared chunk (specified by its offset and length) is valid within the 
+chunk and corresponds to a region of committed memory.
+@param aChunkOffset Offset of the region from the beginning of the chunk.
+@param aLength The length in bytes of the region.
+@param anAudioBuffer A reference to a pointer to an audio buffer object. On return this will either contain a pointer
+to the audio buffer object which corresonds to the specified region, or NULL if the region is invalid.
+@return KErrNone if the region is valid, otherwise KErrArgument.
+*/	
+TInt DBufferManager::ValidateRegion(TUint aChunkOffset,TUint aLength,TAudioBuffer*& anAudioBuffer)
+	{
+	
+	TUint bufStart;
+	TUint bufEnd;
+	TUint regEnd=(aChunkOffset+aLength);
+	for (TInt i=0 ; i<iNumBuffers ; i++)
+		{
+		bufStart=iAudioBuffers[i].iChunkOffset;
+		bufEnd=iAudioBuffers[i].iChunkOffset+iAudioBuffers[i].iSize;
+		if (aChunkOffset<bufStart || aChunkOffset>=bufEnd)
+			continue;
+		if (regEnd<=bufEnd)
+			{
+			anAudioBuffer=&iAudioBuffers[i];
+			return(KErrNone);
+			}
+		}
+	return(KErrArgument);
+	}
+			
+/**
+Commit memory for a single buffer within the shared chunk.
+@param aChunkOffset The offset (in bytes) from start of chunk, which indicates the start of the memory region to be
+	committed. Must be a multiple of the MMU page size. 
+@param aSize The number of bytes to commit. Must be a multiple of the MMU page size.
+@param aIsContiguous On return, this is set to ETrue if the function succeeded in committing physically contiguous memory;
+	EFalse if the committed memory is not contiguous.	
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+*/	
+TInt DBufferManager::CommitMemoryForBuffer(TInt aChunkOffset,TInt aSize,TBool& aIsContiguous)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::CommitMemoryForBuffer(Offset-%x,Sz-%d)",aChunkOffset,aSize));
+	
+	// Try for physically contiguous memory first.
+	TInt r;	
+	TPhysAddr physicalAddress;
+	r=Kern::ChunkCommitContiguous(iChunk,aChunkOffset,aSize,physicalAddress);
+	if (r==KErrNone)
+		{
+		aIsContiguous=ETrue;
+		return(r);
+		}
+			
+	// Try to commit memory that isn't contiguous instead.
+	aIsContiguous=EFalse;
+	r=Kern::ChunkCommit(iChunk,aChunkOffset,aSize);
+	return(r);
+	}
+	
+/**
+Purge a region of the audio chunk. That is, if this region contains cacheable memory, flush it.
+@param aChunkOffset The offset within the chunk for the start of the data to be flushed.
+@param aLength The length in bytes of the region to be flushed.
+@param aFlushOp The type of flush operation required - @see TFlushOp.
+*/
+void DBufferManager::FlushData(TInt aChunkOffset,TInt aLength,TFlushOp aFlushOp)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::FlushData(%d)",aFlushOp));
+	TLinAddr dataAddr=(iChunkBase+aChunkOffset);
+	switch (aFlushOp)
+		{
+		case EFlushBeforeDmaWrite:
+			Cache::SyncMemoryBeforeDmaWrite(dataAddr,aLength,iChunkMapAttr);
+			break;
+		case EFlushBeforeDmaRead:
+			Cache::SyncMemoryBeforeDmaRead(dataAddr,aLength,iChunkMapAttr);
+			break;
+		case EFlushAfterDmaRead:
+			Cache::SyncMemoryAfterDmaRead(dataAddr,aLength);
+			break;
+		default:
+			break;
+		}
+	}
+		
+/**
+Constructor for the record buffer manager.
+*/
+DRecordBufferManager::DRecordBufferManager(DSoundScLdd* aLdd)
+	: DBufferManager(aLdd)
+	{
+	}
+			
+/**
+Reset all the audio buffer lists to reflect the state at the start of the record capture process.
+@pre The buffer/request queue mutex must be held.
+*/
+void DRecordBufferManager::Reset()
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::Reset"));
+	TAudioBuffer* pBuf;
+	
+	// Before reseting buffer lists, purge the cache for all cached buffers currently in use by client.
+	pBuf=(TAudioBuffer*)iInUseBufferQ.First();
+	SDblQueLink* anchor=&iInUseBufferQ.iA;
+	while (pBuf!=anchor)
+		{
+		pBuf->Flush(DBufferManager::EFlushBeforeDmaRead);
+		pBuf=(TAudioBuffer*)pBuf->iNext;
+		}
+		
+	// Start by reseting all the lists.
+	iFreeBufferQ.iA.iNext=iFreeBufferQ.iA.iPrev=&iFreeBufferQ.iA;	
+	iCompletedBufferQ.iA.iNext=iCompletedBufferQ.iA.iPrev=&iCompletedBufferQ.iA;
+	iInUseBufferQ.iA.iNext=iInUseBufferQ.iA.iPrev=&iInUseBufferQ.iA;	
+		
+	// Set the pointers to the current and the next record buffers.
+	pBuf=iAudioBuffers; 		// This is the first buffer
+	iCurrentBuffer=pBuf++;
+	iNextBuffer = pBuf++;
+	
+	// Add all other buffers to the free list.
+	TAudioBuffer* bufferLimit=iAudioBuffers+iNumBuffers; 
+	while(pBuf<bufferLimit)
+		iFreeBufferQ.Add(pBuf++);
+	
+	iBufOverflow=EFalse;	
+	}
+	
+/**
+Update buffer lists after a record buffer has been filled.
+@param aBytesAdded The number of bytes added to the buffer to get it into the 'filled' state. Of course, this is
+	normally equal to the size of the buffer. The exception is when recording has been paused: in which
+	case the number of bytes added to 'fill' the buffer may be less than the buffer size.
+@param aTransferResult The result of the transfer.	
+@return A pointer to the next buffer for recording.
+@pre The buffer/request queue mutex must be held.
+*/
+TAudioBuffer* DRecordBufferManager::SetBufferFilled(TInt aBytesAdded,TInt aTransferResult)
+	{
+	// If record has been paused then its possible (depending on the PDD implementation) that although the current
+	// buffer is marked as being filled, no data has been added. If this is the case then there is no point in informing 
+	// the client about it. Instead we need to return it to the free list. Otherwise the more normal course of action is
+	// to add the current buffer to the completed list ready for the client. If there is any amount of data in the record
+	// buffer, this needs to be passed to the client (and if we're not paused then each buffer should actually be full).
+	// If an error occured then we always add the buffer to the completed list.
+	TAudioBuffer* buffer=iCurrentBuffer;
+	if (aBytesAdded || aTransferResult)
+		{
+		buffer->iBytesAdded=aBytesAdded;
+		buffer->iResult=aTransferResult;
+		iCompletedBufferQ.Add(buffer);
+		}
+	else
+		iFreeBufferQ.Add(buffer);
+
+	// Make the pending buffer the current one.
+	iCurrentBuffer=iNextBuffer;
+
+	// Obtain the next pending buffer. If there are none left on the free list then we have to take one back
+	// from the completed list.
+	iNextBuffer=(TAudioBuffer*)iFreeBufferQ.GetFirst();
+	if (!iNextBuffer)
+		{
+		iNextBuffer=(TAudioBuffer*)iCompletedBufferQ.GetFirst();
+		iBufOverflow=ETrue;	// This is the buffer overflow situation.
+		}
+	__ASSERT_DEBUG(iNextBuffer,Kern::Fault(KSoundLddPanic,__LINE__));	
+	iNextBuffer->iBytesAdded=0;
+	
+	__KTRACE_OPT(KSOUND1, Kern::Printf("<DBufferManager::SetBufferFilled(buf=%08x len=%d)",buffer->iChunkOffset,buffer->iBytesAdded));
+	return(iNextBuffer);
+	}
+
+/**
+Get the next record buffer from the completed buffer list. If there is no error associated with the buffer, 
+make it 'in use' by the client. Otherwise, return the buffer to the free list.
+@return A pointer to the next completed buffer or NULL if there isn't one available.
+@pre The buffer/request queue mutex must be held.
+*/
+TAudioBuffer* DRecordBufferManager::GetBufferForClient()
+	{	
+	TAudioBuffer* buffer=(TAudioBuffer*)iCompletedBufferQ.GetFirst();
+	if (buffer)
+		{
+		if (buffer->iResult==KErrNone)
+			iInUseBufferQ.Add(buffer);
+		else
+			iFreeBufferQ.Add(buffer);
+		}
+	
+	__KTRACE_OPT(KSOUND1, Kern::Printf("<DBufferManager::BufferForClient(buf=%08x)",(buffer ? buffer->iChunkOffset : -1)));
+	return(buffer);
+	}
+
+/**
+Release (move to free list) the 'in use' record buffer specified by the given chunk offset.
+@param aChunkOffset The chunk offset corresponding to the buffer to be freed.
+@return The freed buffer, or NULL if no 'in use' buffer had the specified chunk offset.
+@pre The buffer/request queue mutex must be held.
+*/
+TAudioBuffer* DRecordBufferManager::ReleaseBuffer(TInt aChunkOffset)
+	{
+	// Scan 'in use' list for the audio buffer
+	TAudioBuffer* pBuf;
+	pBuf=(TAudioBuffer*)iInUseBufferQ.First();
+	SDblQueLink* anchor=&iInUseBufferQ.iA;
+	while (pBuf!=anchor && pBuf->iChunkOffset!=aChunkOffset)
+		pBuf=(TAudioBuffer*)pBuf->iNext;
+	
+	// Move buffer to the free list (if found)
+	if (pBuf!=anchor)
+		iFreeBufferQ.Add(pBuf->Deque());
+	else
+		pBuf=NULL;
+	
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">DBufferManager::BufferRelease(buf=%08x)",(pBuf ? pBuf->iChunkOffset : -1)));
+	return(pBuf);
+	}
+
+/**
+Constructor for the audio buffer class.
+Clears all member data
+*/
+TAudioBuffer::TAudioBuffer()
+	{
+	memclr(this,sizeof(*this));
+	}
+
+/**
+Destructor for the audio buffer class.
+*/
+TAudioBuffer::~TAudioBuffer()
+	{
+	delete[] iPhysicalPages;
+	}
+	
+/**
+Second stage constructor for the audio buffer class - validate and acquire information on the memory
+allocated to this buffer.
+@param aChunk  The chunk in which this buffer belongs.
+@param aChunkOffset The offset within aChunk to the start of the audio buffer.
+@param aSize The size (in bytes) of the buffer.
+@param aIsContiguous A boolean indicating whether the buffer contains physically contiguous memory. Set to ETrue if it
+	does physically contiguous memory, EFalse otherwise.		
+@param aBufManager A pointer to the buffer manager which owns this object.
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+@pre The thread must be in a critical section.
+*/
+TInt TAudioBuffer::Create(DChunk* aChunk,TInt aChunkOffset,TInt aSize,TBool aIsContiguous,DBufferManager* aBufManager)
+	{
+	__KTRACE_OPT(KSOUND1, Kern::Printf(">TAudioBuffer::Create(Off-%x,Sz-%d,Contig-%d)",aChunkOffset,aSize,aIsContiguous));
+
+	// Save info. on the offset and size of the buffer. Also the buffer manager.
+	iChunkOffset=aChunkOffset;
+	iSize=aSize;
+	iBufManager=aBufManager;
+
+	TInt r=KErrNone;
+	iPhysicalPages=NULL;
+	if (!aIsContiguous)
+		{
+		// Allocate an array for a list of the physical pages.
+		iPhysicalPages = new TPhysAddr[aSize/Kern::RoundToPageSize(1)+2];
+		if (!iPhysicalPages)
+			r=KErrNoMemory;	
+		}
+		
+	if (r==KErrNone)
+		{
+		// Check that the region of the chunk specified for the buffer contains committed memory. If so, get the physical addresses of the
+		// pages in this buffer.
+		TUint32 kernAddr;
+		TUint32 mapAttr;
+		r=Kern::ChunkPhysicalAddress(aChunk,aChunkOffset,aSize,kernAddr,mapAttr,iPhysicalAddress,iPhysicalPages);
+		// r = 0 or 1 on success. (1 meaning the physical pages are not contiguous).
+		if (r==1)
+			{
+			// The physical pages are not contiguous.
+			iPhysicalAddress=KPhysAddrInvalid;	// Mark the physical address as invalid.
+			r=(aIsContiguous) ? KErrGeneral : KErrNone;
+			}
+		if (r==0)
+			{
+			delete[] iPhysicalPages;	// We shouldn't retain this info. if the physical pages are contiguous.
+			iPhysicalPages=NULL;
+			}
+			
+		}
+	__KTRACE_OPT(KSOUND1, Kern::Printf("<TAudioBuffer::Create - %d",r));
+	return(r);
+	}
+
+/**
+Calculate the length for the next part of a data transfer request so that that the length returned specifies a physically
+contiguous region and is also valid for the PDD. If necessary, return a truncated length that meets these criteria. Also,
+return the physical address of the start of the region specified.
+@param aChunkOffset The offset within the chunk for the start of the data transfer being fragmented.
+@param aLengthRemaining The remaining length of the data transfer request.
+@param aPhysAddr On return, this contains the physical address corresonding to aChunkOffset.
+@return The length calculated.
+*/	
+TInt TAudioBuffer::GetFragmentLength(TInt aChunkOffset,TInt aLengthRemaining,TPhysAddr& aPhysAddr)		
+	{
+	TInt len;
+	TInt bufOffset=(aChunkOffset - iChunkOffset); 	// Convert from chunk offset to buffer offset.
+	
+	if (iPhysicalAddress==KPhysAddrInvalid)
+		{
+		// Buffer is not physically contiguous. Truncate length to the next page boundary. Then calculate physical addr.
+		// (This function doesn't look for pages which are contiguous within the physical page list - but it could).
+		TInt pageSize=Kern::RoundToPageSize(1);
+		TInt pageOffset=bufOffset%pageSize;
+		len=pageSize - pageOffset;
+		aPhysAddr=iPhysicalPages[bufOffset/pageSize] + pageOffset;
+		}
+	else
+		{
+		// Buffer is physically contiguous so no need to truncate the length. Then calculate physical address.  
+		len=aLengthRemaining;
+		aPhysAddr=iPhysicalAddress + bufOffset;
+		}
+		
+	// Ensure length does not exceed the max. supported by the PDD.
+	len=Min(iBufManager->iMaxTransferLen,len);		
+	return(len);
+	}
+	
+/**
+Purge the entire audio buffer. That is, if it contains cacheable memory, flush it.
+@param aFlushOp The type of flush operation required - @see DBufferManager::TFlushOp.
+*/
+void TAudioBuffer::Flush(DBufferManager::TFlushOp aFlushOp)
+	{
+	iBufManager->FlushData(iChunkOffset,iSize,aFlushOp);
+	}
+