diff -r 72a7468afdd4 -r 01c1ffcc4fca baseport/syborg/soundsc/virtio_queue.cpp --- /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(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(descIdiIdx - 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( 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( iMemAligned ); + iAvail = reinterpret_cast( iMemAligned + iDescSize ); + iUsed = reinterpret_cast( iMemAligned + usedOffset ); + iToken = reinterpret_cast( 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