baseport/syborg/soundsc/virtio_queue.cpp
changeset 45 01c1ffcc4fca
child 71 d00bf4f57250
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/baseport/syborg/soundsc/virtio_queue.cpp	Thu Mar 04 00:55:21 2010 +0000
@@ -0,0 +1,328 @@
+/*
+* 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_queue.h"
+
+#define ENABLE_QEMU_AUDIO_MODEL_BUG_WORKAROUND
+#define ENABLE_QEMU_VIRTIO_CLEANUP_BUG_WORKAROUND
+#define PHYS_ADDR(x) Epoc::LinearToPhysical( reinterpret_cast<TUint32>(x))
+	
+namespace VirtIo
+{
+
+
+DQueue::DQueue( MIo& aVirtIo, TUint aId, TUint aCount ) 
+	: iVirtIo( aVirtIo ), iId( aId ), iCount( aCount )
+	{
+	}
+	
+TInt DQueue::Construct()
+	{
+	TInt r = AllocQueue();
+	if (r!=KErrNone)
+		{ return r; }
+	PreInitQueue();
+	iVirtIo.SetQueueBase( iId, iDesc );
+	PostInitQueue();
+	return KErrNone;
+	}
+
+void DQueue::Wipe()
+	{
+	if ( iChunk )
+		{
+		iChunk->Close( NULL );
+		iChunk = NULL;
+		}
+#ifndef ENABLE_QEMU_VIRTIO_CLEANUP_BUG_WORKAROUND		
+	if ( iPhysAddr )
+		{ Epoc::FreePhysicalRam( iPhysAddr ); }
+#endif
+	iPhysAddr = 0;
+	}
+	
+DQueue::~DQueue()
+	{
+	Wipe();
+	}	
+	
+//
+// Implementation here is that descs are allocated from the one starting with lowest index, 
+TInt DQueue::AddBuf( const TAddrLen aScatterList[], TUint32 aOutNum, TUint32 aInNum, Token aToken)
+	{
+	SYBORG_VIRTIO_DEBUG("AddBuf Q%x %x l%x %x,%x %x",
+		iId, aScatterList[0].iAddr, aScatterList[0].iLen, aOutNum, aInNum, aToken );
+	TUint outLeft = aOutNum;
+	TUint inLeft = aInNum;
+	TInt first = -1;
+	TInt last = -1;
+	TUint totalLen = 0;
+	// @TODO maintain freedescs counter to know if the below is going to success from the outset.
+	// alloc and initialise descs
+	for ( TUint i = 0, j = 0; (i < iCount) && (outLeft+inLeft); ++i )
+		{
+		if (iDesc[i].iFlags == KFreeDescriptorMarker)
+			{
+			if (first<0)
+				{ first = i; }
+			iDesc[i].iFlags = ((outLeft)? 0 : TRingDesc::EFlagWrite);
+			iDesc[i].iNext = KFreeDescriptorMarker;
+			iDesc[i].iAddr = aScatterList[j].iAddr;
+			iDesc[i].iLen = aScatterList[j].iLen;
+			totalLen += aScatterList[j].iLen;
+			iToken[i].iToken = aToken;
+			iToken[i].iTotalLen = 0;
+			if ( last >=0 )
+				{ 
+				iDesc[last].iNext = i; 
+				iDesc[last].iFlags |= TRingDesc::EFlagNext;
+				}
+			last = i;
+			if (outLeft) 
+				{ --outLeft; }
+			else 
+				{ --inLeft; }
+			j++;
+			}
+		}
+	if (outLeft+inLeft) // rollback descs if not all could have been claimed
+		{
+		if (first>=0)
+			{ FreeDescs(first); }
+		return KErrNotReady;
+		}
+	iToken[first].iTotalLen = totalLen;
+	
+	// fill a slot in avail ring
+	iAvail->iRing[Slot(iAvail->iIdx)] = first;
+	++iAvail->iIdx;
+	
+	return KErrNone;
+	}	
+	
+// bases on the assumption that the lowest desc index with given aToken value is used (see addBuf)
+// @todo make sure the buffer is not yet posted	
+TInt DQueue::DetachBuf( Token aToken )
+	{
+	TUint myDescId = KFreeDescriptorMarker;
+	for ( TIdx i = iNextAvailToSync; i != iAvail->iIdx ; ++i )
+		{
+		TUint availSlot = Slot( i );
+		TUint descId = iAvail->iRing[availSlot];
+		if ( descId < iCount )
+			{
+			if (iToken[descId].iToken == aToken)
+				{
+				myDescId = descId;
+				break;
+				}
+			}
+		}
+	if ( myDescId != KFreeDescriptorMarker )
+		{ return KErrNotFound; }
+	FreeDescs( myDescId );
+	return KErrNone;
+	}	
+	
+Token DQueue::GetBuf( TUint& aLen )
+	{
+	TIdx usedIdx = iUsed->iIdx;
+	ASSERT( ((TIdx)iNextUsedToRead) <= usedIdx );
+	if (usedIdx == ((TIdx)iNextUsedToRead))
+		{ return 0; }
+	TUint nextUsedSlot = Slot(iNextUsedToRead);
+	TUint len = iUsed->iRing[nextUsedSlot].iLen;
+	TUint descId = iUsed->iRing[nextUsedSlot].iId;
+	ASSERT(descId<iCount);
+	Token token = iToken[descId].iToken;	
+	TUint orderedLen = iToken[descId].iTotalLen;	
+	SYBORG_VIRTIO_DEBUG( "GetBuf Q%x %x ..%x t%x D%x L%x OL%x", iId, iNextUsedToRead, usedIdx, token, descId, len, orderedLen );
+
+	++iNextUsedToRead;
+	FreeDescs( descId );
+	
+#ifdef ENABLE_QEMU_AUDIO_MODEL_BUG_WORKAROUND	
+	aLen = len?len:orderedLen; // @TODO kind of a hack to solve virtio-audio's failure to report len by syborg on the side of qemu
+#endif
+	return token;
+	}	
+	
+TInt DQueue::Processing()
+	{ return  ((TIdx)(iAvail->iIdx - iFirstEverToSync)) 
+		- ((TIdx)(iUsed->iIdx - iFirstEverToRead)); }	
+	
+TInt DQueue::Completed()
+	{ return  ((TIdx)(iUsed->iIdx - iNextUsedToRead)); }		
+
+void DQueue::Sync()
+	{
+	SYBORG_VIRTIO_DEBUG("Sync Q%d, %x..%x",
+		iId, iNextAvailToSync, iAvail->iIdx );	
+	if ( ((TIdx)iNextAvailToSync) == iAvail->iIdx)
+		{ return; }
+	DumpAvailPending();
+
+	iNextAvailToSync = iAvail->iIdx;
+	iVirtIo.PostQueue( iId );
+	}
+	
+void DQueue::DumpUsedPending()
+	{
+	for (TIdx i = iNextUsedToRead; i != iUsed->iIdx; ++i )
+		{ DumpUsed( Slot(i) ); }
+	}	
+	
+void DQueue::DumpUsed(TUint usedSlot)
+	{
+	SYBORG_VIRTIO_DEBUG("Usedslot = %x, aLen = %x, descId=%x", usedSlot, (TUint32) iUsed->iRing[usedSlot].iLen, iUsed->iRing[usedSlot].iId);
+	TUint descId = iUsed->iRing[usedSlot].iId;
+	DumpDescs( descId );
+	}	
+	
+void DQueue::DumpAvailPending()
+	{
+	for (TIdx i = iNextAvailToSync; i != iAvail->iIdx; ++i )
+		{ DumpAvail( Slot(i) ); }
+	}
+	
+void DQueue::DumpAvail(TUint availSlot)
+	{
+	SYBORG_VIRTIO_DEBUG("Q%d, availslot = %x", iId, availSlot);
+	TUint descId = iAvail->iRing[availSlot];
+	DumpDescs( descId );
+	}
+	
+void DQueue::DumpDescs(TUint descId )
+	{
+	do {
+		TRingDesc& d = iDesc[descId];
+		SYBORG_VIRTIO_DEBUG(" Desc %x,addr %x, len %x, flags %x, next %x",
+			(TUint32)descId, (TUint32)d.iAddr, (TUint32)d.iLen, (TUint32)d.iFlags, (TUint32)d.iNext );
+		if ((d.iFlags&TRingDesc::EFlagNext)==0)
+			{ break; }
+		descId = d.iNext;
+		} while (ETrue);
+	}
+	
+void DQueue::DumpAll()
+	{
+	DumpAvailPending();
+	DumpUsedPending();
+	}	
+		
+void DQueue::FreeDescs( TUint firstDescIdx )
+	{
+	TInt i = firstDescIdx;
+	Token token = iToken[firstDescIdx].iToken;
+	while (i>=0)
+		{
+		ASSERT( ( ((TUint)i) < iCount ) );
+		TUint flags = iDesc[i].iFlags;
+		ASSERT( flags != KFreeDescriptorMarker );
+		iDesc[i].iFlags = KFreeDescriptorMarker;
+		ASSERT( iToken[i].iToken == token );
+		iToken[i].iToken = 0;
+		iToken[i].iTotalLen = 0;
+		i = (flags&TRingDesc::EFlagNext)
+			? iDesc[i].iNext : -1;
+		}
+	}		
+	
+TUint8* DQueue::AllocMem( TUint aSize )
+	{
+	TInt r = KErrNone;
+	
+#ifdef ENABLE_QEMU_VIRTIO_CLEANUP_BUG_WORKAROUND		
+	// note this is second part of workaround that deals
+	// with the issue that memory once assigned to queuebase cannot be 
+	// changed,
+	// if queuebase != 0 this is because it was set when the driver
+	// was loaded previous time
+
+	iPhysAddr = (TUint32) iVirtIo.GetQueueBase( iId );
+	iPhysMemReallyAllocated = (iPhysAddr == 0);
+	if (iPhysMemReallyAllocated)
+		{ r = Epoc::AllocPhysicalRam(aSize, iPhysAddr, 0 ); }
+
+#endif
+
+	if (r!=KErrNone ) 
+		{ return NULL; }
+	
+	r = DPlatChunkHw::New( iChunk, iPhysAddr, aSize, 
+		EMapAttrSupRw | EMapAttrL2Uncached | EMapAttrL1Uncached );
+
+	if (r!=KErrNone ) 
+		{
+		if ( iPhysMemReallyAllocated )
+			{ Epoc::FreePhysicalRam( iPhysAddr, aSize ); }
+		iChunk->Close( NULL );
+		return NULL;
+		}
+
+	ASSERT( r == KErrNone );
+	return reinterpret_cast<TUint8*>( iChunk->LinearAddress() );
+	}	
+	
+TInt DQueue::AllocQueue()
+	{
+	iDescSize = iCount * sizeof(TRingDesc);
+	iAvailSize = sizeof(TRingAvail) + (iCount-1) * sizeof(((TRingAvail*)0)->iRing[0]);
+	iTokenSize = iCount * sizeof(TTransactionInfo);
+	TUint usedOffset = Align( iDescSize +  iAvailSize, KVirtIoAlignment );
+	TUint iUsedSize = sizeof(TRingUsed) + (iCount-1) * sizeof(((TRingUsed*)0)->iRing[0]);
+	TUint size = usedOffset + iUsedSize;
+	TUint8* iMemAligned;
+
+	iMemAligned = AllocMem( size );
+	
+	if (!iMemAligned)
+		{ return KErrNoMemory; }
+	
+	iDesc = reinterpret_cast<TRingDesc*>( iMemAligned );
+	iAvail = reinterpret_cast<TRingAvail*>( iMemAligned + iDescSize );
+	iUsed = reinterpret_cast<TRingUsed*>( iMemAligned + usedOffset );
+	iToken = reinterpret_cast<TTransactionInfo*>( Kern::Alloc( iTokenSize ) );
+	SYBORG_VIRTIO_DEBUG("DQueue %d, Virt iDesc=%x,iAvail=%x,iToken=%x,iUsed=%x",
+		iId, iDesc, iAvail, iToken, iUsed );
+	SYBORG_VIRTIO_DEBUG("DQueue %d, Phys iDesc=%x, iUsed=%x",
+		iId, PHYS_ADDR(iDesc), PHYS_ADDR(iUsed) );
+	ASSERT( ((PHYS_ADDR(iUsed)-PHYS_ADDR(iDesc))) == ((TUint32)((TUint8*)iUsed-(TUint8*)iDesc)) );
+	return KErrNone;
+	}	
+	
+void DQueue::PreInitQueue()
+	{
+	memset(iDesc, -1, iDescSize );
+	memset( ((TUint8*) iAvail) + 4, -1, iDescSize - 4 );
+	memset( ((TUint8*) iUsed) + 4, -1, iDescSize - 4 );
+	
+	iAvail->iFlags = 0; // no notifications from control queue
+	iUsed->iFlags = 0;
+	if ( iPhysMemReallyAllocated )
+		{
+		iAvail->iIdx = 0;
+		iUsed->iIdx = 0;
+		}
+	}	
+	
+void DQueue::PostInitQueue()
+	{
+	iFirstEverToSync = iNextAvailToSync = iAvail->iIdx;
+	iFirstEverToRead = iNextUsedToRead = iUsed->iIdx;
+	}		
+
+
+} // namespace VirtIo