baseport/syborg/soundsc/virtio_audio.cpp
author Maciej Jablonski <maciej.jablonski@accenture.com>
Thu, 04 Mar 2010 00:55:21 +0000
changeset 45 01c1ffcc4fca
child 71 d00bf4f57250
permissions -rw-r--r--
Audio driver for qemu simulator

/*
* 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:
* Accenture Ltd
*
* Contributors:
*
* Description: This file is a part of sound driver for Syborg adaptation.
*
*/

#include "virtio_audio.h"

namespace VirtIo
{
namespace Audio
{

DControl::~DControl()
	{
	if (iCmdMem)
		{ Kern::Free( iCmdMem ); }
	}

TInt DControl::Construct()
	{
	TUint cmdSize = sizeof(iCmd[0])*KCmdMaxBufCount;
	TUint size = cmdSize + sizeof(*iBufferInfo);
	// as we are going to align the allocated memory
	// we add extra alignment size minus 1
	iCmdMem = reinterpret_cast<TUint8*>( Kern::Alloc( size + sizeof(iCmd[0])-1 ) );
	if (!iCmdMem)
		{ return KErrNoMemory; }
		
	// let us align the memory address 
	// note: sizeof(iCmd[0]) is a power of 2
	ASSERT( sizeof(iCmd[0]) == POW2ALIGN(sizeof(iCmd[0])) );
	TUint8* alignedMem = iCmdMem 
		+ ( (-(TUint32)iCmdMem)&(sizeof(iCmd[0])-1) ); // adding missing amount of bytes
	
	iCmd = reinterpret_cast<TCommandPadded*>( alignedMem );
	iBufferInfo = reinterpret_cast<TBufferInfo*>( alignedMem + cmdSize );
	
	for (TUint i = 0; i< KCmdMaxBufCount; ++i)
		{
		iCmd[i].iStream = iDataQueueId - 1;
		}	
	iCmd[0].iCommand = Audio::ECmdSetEndian;
	iCmd[1].iCommand = Audio::ECmdSetChannels;
	iCmd[2].iCommand = Audio::ECmdSetFormat;
	iCmd[3].iCommand = Audio::ECmdSetFrequency;
	iCmd[4].iCommand = Audio::ECmdInit;
	iCmd[5].iCommand = Audio::ECmdRun;
	iCmd[5].iArg = Audio::EDoRun; //start stream
	iCmd[6].iCommand = Audio::ECmdRun;
	iCmd[6].iArg = Audio::EDoStop; //stop stream		
	iCmd[7].iCommand = Audio::ECmdRun;
	iCmd[7].iArg = Audio::EDoStop; //kind of pause
	iCmd[8].iCommand = Audio::ECmdRun;
	iCmd[8].iArg = Audio::EDoRun; //kind of resume
	return KErrNone;
	}

TInt DControl::Setup( StreamDirection aDirection, TInt aChannelNum, 
	FormatId aFormat, TInt aFreq)
	{
	iCmd[1].iArg = aChannelNum;
	iCmd[2].iArg = aFormat;
	iCmd[3].iArg = aFreq;		
	iCmd[4].iArg = iDirection = aDirection;		
	AddCommand(&iCmd[0],(Token)0);
	AddCommand(&iCmd[1],(Token)1);
	AddCommand(&iCmd[2],(Token)2);
	AddCommand(&iCmd[3],(Token)3);
	AddCommand(&iCmd[4],(Token)4, iBufferInfo, sizeof(*iBufferInfo) );
	ControlQueue().Sync();
	return KErrNone;
	}
	
void DControl::AddCommand( TCommandPadded* aCmd, Token aToken )
	{
	TAddrLen list;
	list.iLen = sizeof(TCommand);
	list.iAddr = Epoc::LinearToPhysical((TUint32)aCmd);
	SYBORG_VIRTIO_DEBUG("AddCommand %x %x %x", aCmd->iCommand, aCmd->iStream, aCmd->iArg);
	ControlQueue().AddBuf(&list, 1, 0, aToken );
	}

void DControl::AddCommand( TCommandPadded* aCmd, Token aToken, 
	TAny* aMem, TUint aSize )
	{
	TAddrLen list[2];
	list[0].iLen = sizeof(TCommand);
	list[0].iAddr = Epoc::LinearToPhysical((TUint32)aCmd);
	list[1].iLen = aSize;
	list[1].iAddr = Epoc::LinearToPhysical((TUint32)aMem);
	ControlQueue().AddBuf(list, 1, 1, aToken );
	}
	
	
// Waits until device processes all pending requests
// there would be no need to have it here at all 
// if there was no bug in qemu:
// once you send stop command the buffers processing stops... but the buffers are never returned.

void DControl::WaitForCompletion()
	{
	SYBORG_VIRTIO_DEBUG("DControl::WaitForCompletion : {");

	TInt st = Kern::PollingWait( &DControl::CheckProcessing, this, 10, 100 );
	ASSERT ( (st == KErrNone) && "Polling problem" )
	
	SYBORG_VIRTIO_DEBUG("DControlWaitForCompletion : }");
	}

TBool DControl::CheckProcessing( TAny* aSelf )
	{
	DControl* self = reinterpret_cast<DControl*>( aSelf );
	return self->DataQueue().Processing() == 0;
	}
	
void DControl::AddCommand( Command aCmd )
	{
	TUint idx = aCmd;
	if (aCmd == EStop)
		{
		// due to bug on qemu's side we need to stop sending buffers
		// and wait for all pending buffers to get filled...
		WaitForCompletion();
		}
	AddCommand(&iCmd[idx], (Token)idx );
	}

TInt DControl::SendDataBuffer( TAny* virtaulAddr, TUint aSize, Token aToken )
	{
	TAddrLen sgl[KMaxSGLItemCountPerAudioBuffer];
	TUint sglCount = KMaxSGLItemCountPerAudioBuffer;
	TInt st = LinearToSGL(virtaulAddr, aSize, sgl, sglCount );
	ASSERT( st != KErrNoMemory ); // failure means our fixed lenght sgl table is too small
	if (st!=KErrNone)
		{ return st; }
	st = DataQueue().AddBuf( sgl, 
		iDirection == EDirectionPlayback ? sglCount : 0,
		iDirection == EDirectionRecord ? sglCount : 0,
		aToken );
	if (st!=KErrNone)
		{ return st; }
	DataQueue().Sync();
	return KErrNone;
	}
	

} // namespace Audio
} // namespace VirtIo