baseport/syborg/soundsc/virtio_queue.cpp
changeset 45 01c1ffcc4fca
child 72 d00bf4f57250
equal deleted inserted replaced
44:72a7468afdd4 45:01c1ffcc4fca
       
     1 /*
       
     2 * This component and the accompanying materials are made available
       
     3 * under the terms of the License "Eclipse Public License v1.0"
       
     4 * which accompanies this distribution, and is available
       
     5 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     6 *
       
     7 * Initial Contributors:
       
     8 * Accenture Ltd
       
     9 *
       
    10 * Contributors:
       
    11 *
       
    12 * Description: This file is a part of sound driver for Syborg adaptation.
       
    13 *
       
    14 */
       
    15 
       
    16 #include "virtio_queue.h"
       
    17 
       
    18 #define ENABLE_QEMU_AUDIO_MODEL_BUG_WORKAROUND
       
    19 #define ENABLE_QEMU_VIRTIO_CLEANUP_BUG_WORKAROUND
       
    20 #define PHYS_ADDR(x) Epoc::LinearToPhysical( reinterpret_cast<TUint32>(x))
       
    21 	
       
    22 namespace VirtIo
       
    23 {
       
    24 
       
    25 
       
    26 DQueue::DQueue( MIo& aVirtIo, TUint aId, TUint aCount ) 
       
    27 	: iVirtIo( aVirtIo ), iId( aId ), iCount( aCount )
       
    28 	{
       
    29 	}
       
    30 	
       
    31 TInt DQueue::Construct()
       
    32 	{
       
    33 	TInt r = AllocQueue();
       
    34 	if (r!=KErrNone)
       
    35 		{ return r; }
       
    36 	PreInitQueue();
       
    37 	iVirtIo.SetQueueBase( iId, iDesc );
       
    38 	PostInitQueue();
       
    39 	return KErrNone;
       
    40 	}
       
    41 
       
    42 void DQueue::Wipe()
       
    43 	{
       
    44 	if ( iChunk )
       
    45 		{
       
    46 		iChunk->Close( NULL );
       
    47 		iChunk = NULL;
       
    48 		}
       
    49 #ifndef ENABLE_QEMU_VIRTIO_CLEANUP_BUG_WORKAROUND		
       
    50 	if ( iPhysAddr )
       
    51 		{ Epoc::FreePhysicalRam( iPhysAddr ); }
       
    52 #endif
       
    53 	iPhysAddr = 0;
       
    54 	}
       
    55 	
       
    56 DQueue::~DQueue()
       
    57 	{
       
    58 	Wipe();
       
    59 	}	
       
    60 	
       
    61 //
       
    62 // Implementation here is that descs are allocated from the one starting with lowest index, 
       
    63 TInt DQueue::AddBuf( const TAddrLen aScatterList[], TUint32 aOutNum, TUint32 aInNum, Token aToken)
       
    64 	{
       
    65 	SYBORG_VIRTIO_DEBUG("AddBuf Q%x %x l%x %x,%x %x",
       
    66 		iId, aScatterList[0].iAddr, aScatterList[0].iLen, aOutNum, aInNum, aToken );
       
    67 	TUint outLeft = aOutNum;
       
    68 	TUint inLeft = aInNum;
       
    69 	TInt first = -1;
       
    70 	TInt last = -1;
       
    71 	TUint totalLen = 0;
       
    72 	// @TODO maintain freedescs counter to know if the below is going to success from the outset.
       
    73 	// alloc and initialise descs
       
    74 	for ( TUint i = 0, j = 0; (i < iCount) && (outLeft+inLeft); ++i )
       
    75 		{
       
    76 		if (iDesc[i].iFlags == KFreeDescriptorMarker)
       
    77 			{
       
    78 			if (first<0)
       
    79 				{ first = i; }
       
    80 			iDesc[i].iFlags = ((outLeft)? 0 : TRingDesc::EFlagWrite);
       
    81 			iDesc[i].iNext = KFreeDescriptorMarker;
       
    82 			iDesc[i].iAddr = aScatterList[j].iAddr;
       
    83 			iDesc[i].iLen = aScatterList[j].iLen;
       
    84 			totalLen += aScatterList[j].iLen;
       
    85 			iToken[i].iToken = aToken;
       
    86 			iToken[i].iTotalLen = 0;
       
    87 			if ( last >=0 )
       
    88 				{ 
       
    89 				iDesc[last].iNext = i; 
       
    90 				iDesc[last].iFlags |= TRingDesc::EFlagNext;
       
    91 				}
       
    92 			last = i;
       
    93 			if (outLeft) 
       
    94 				{ --outLeft; }
       
    95 			else 
       
    96 				{ --inLeft; }
       
    97 			j++;
       
    98 			}
       
    99 		}
       
   100 	if (outLeft+inLeft) // rollback descs if not all could have been claimed
       
   101 		{
       
   102 		if (first>=0)
       
   103 			{ FreeDescs(first); }
       
   104 		return KErrNotReady;
       
   105 		}
       
   106 	iToken[first].iTotalLen = totalLen;
       
   107 	
       
   108 	// fill a slot in avail ring
       
   109 	iAvail->iRing[Slot(iAvail->iIdx)] = first;
       
   110 	++iAvail->iIdx;
       
   111 	
       
   112 	return KErrNone;
       
   113 	}	
       
   114 	
       
   115 // bases on the assumption that the lowest desc index with given aToken value is used (see addBuf)
       
   116 // @todo make sure the buffer is not yet posted	
       
   117 TInt DQueue::DetachBuf( Token aToken )
       
   118 	{
       
   119 	TUint myDescId = KFreeDescriptorMarker;
       
   120 	for ( TIdx i = iNextAvailToSync; i != iAvail->iIdx ; ++i )
       
   121 		{
       
   122 		TUint availSlot = Slot( i );
       
   123 		TUint descId = iAvail->iRing[availSlot];
       
   124 		if ( descId < iCount )
       
   125 			{
       
   126 			if (iToken[descId].iToken == aToken)
       
   127 				{
       
   128 				myDescId = descId;
       
   129 				break;
       
   130 				}
       
   131 			}
       
   132 		}
       
   133 	if ( myDescId != KFreeDescriptorMarker )
       
   134 		{ return KErrNotFound; }
       
   135 	FreeDescs( myDescId );
       
   136 	return KErrNone;
       
   137 	}	
       
   138 	
       
   139 Token DQueue::GetBuf( TUint& aLen )
       
   140 	{
       
   141 	TIdx usedIdx = iUsed->iIdx;
       
   142 	ASSERT( ((TIdx)iNextUsedToRead) <= usedIdx );
       
   143 	if (usedIdx == ((TIdx)iNextUsedToRead))
       
   144 		{ return 0; }
       
   145 	TUint nextUsedSlot = Slot(iNextUsedToRead);
       
   146 	TUint len = iUsed->iRing[nextUsedSlot].iLen;
       
   147 	TUint descId = iUsed->iRing[nextUsedSlot].iId;
       
   148 	ASSERT(descId<iCount);
       
   149 	Token token = iToken[descId].iToken;	
       
   150 	TUint orderedLen = iToken[descId].iTotalLen;	
       
   151 	SYBORG_VIRTIO_DEBUG( "GetBuf Q%x %x ..%x t%x D%x L%x OL%x", iId, iNextUsedToRead, usedIdx, token, descId, len, orderedLen );
       
   152 
       
   153 	++iNextUsedToRead;
       
   154 	FreeDescs( descId );
       
   155 	
       
   156 #ifdef ENABLE_QEMU_AUDIO_MODEL_BUG_WORKAROUND	
       
   157 	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
       
   158 #endif
       
   159 	return token;
       
   160 	}	
       
   161 	
       
   162 TInt DQueue::Processing()
       
   163 	{ return  ((TIdx)(iAvail->iIdx - iFirstEverToSync)) 
       
   164 		- ((TIdx)(iUsed->iIdx - iFirstEverToRead)); }	
       
   165 	
       
   166 TInt DQueue::Completed()
       
   167 	{ return  ((TIdx)(iUsed->iIdx - iNextUsedToRead)); }		
       
   168 
       
   169 void DQueue::Sync()
       
   170 	{
       
   171 	SYBORG_VIRTIO_DEBUG("Sync Q%d, %x..%x",
       
   172 		iId, iNextAvailToSync, iAvail->iIdx );	
       
   173 	if ( ((TIdx)iNextAvailToSync) == iAvail->iIdx)
       
   174 		{ return; }
       
   175 	DumpAvailPending();
       
   176 
       
   177 	iNextAvailToSync = iAvail->iIdx;
       
   178 	iVirtIo.PostQueue( iId );
       
   179 	}
       
   180 	
       
   181 void DQueue::DumpUsedPending()
       
   182 	{
       
   183 	for (TIdx i = iNextUsedToRead; i != iUsed->iIdx; ++i )
       
   184 		{ DumpUsed( Slot(i) ); }
       
   185 	}	
       
   186 	
       
   187 void DQueue::DumpUsed(TUint usedSlot)
       
   188 	{
       
   189 	SYBORG_VIRTIO_DEBUG("Usedslot = %x, aLen = %x, descId=%x", usedSlot, (TUint32) iUsed->iRing[usedSlot].iLen, iUsed->iRing[usedSlot].iId);
       
   190 	TUint descId = iUsed->iRing[usedSlot].iId;
       
   191 	DumpDescs( descId );
       
   192 	}	
       
   193 	
       
   194 void DQueue::DumpAvailPending()
       
   195 	{
       
   196 	for (TIdx i = iNextAvailToSync; i != iAvail->iIdx; ++i )
       
   197 		{ DumpAvail( Slot(i) ); }
       
   198 	}
       
   199 	
       
   200 void DQueue::DumpAvail(TUint availSlot)
       
   201 	{
       
   202 	SYBORG_VIRTIO_DEBUG("Q%d, availslot = %x", iId, availSlot);
       
   203 	TUint descId = iAvail->iRing[availSlot];
       
   204 	DumpDescs( descId );
       
   205 	}
       
   206 	
       
   207 void DQueue::DumpDescs(TUint descId )
       
   208 	{
       
   209 	do {
       
   210 		TRingDesc& d = iDesc[descId];
       
   211 		SYBORG_VIRTIO_DEBUG(" Desc %x,addr %x, len %x, flags %x, next %x",
       
   212 			(TUint32)descId, (TUint32)d.iAddr, (TUint32)d.iLen, (TUint32)d.iFlags, (TUint32)d.iNext );
       
   213 		if ((d.iFlags&TRingDesc::EFlagNext)==0)
       
   214 			{ break; }
       
   215 		descId = d.iNext;
       
   216 		} while (ETrue);
       
   217 	}
       
   218 	
       
   219 void DQueue::DumpAll()
       
   220 	{
       
   221 	DumpAvailPending();
       
   222 	DumpUsedPending();
       
   223 	}	
       
   224 		
       
   225 void DQueue::FreeDescs( TUint firstDescIdx )
       
   226 	{
       
   227 	TInt i = firstDescIdx;
       
   228 	Token token = iToken[firstDescIdx].iToken;
       
   229 	while (i>=0)
       
   230 		{
       
   231 		ASSERT( ( ((TUint)i) < iCount ) );
       
   232 		TUint flags = iDesc[i].iFlags;
       
   233 		ASSERT( flags != KFreeDescriptorMarker );
       
   234 		iDesc[i].iFlags = KFreeDescriptorMarker;
       
   235 		ASSERT( iToken[i].iToken == token );
       
   236 		iToken[i].iToken = 0;
       
   237 		iToken[i].iTotalLen = 0;
       
   238 		i = (flags&TRingDesc::EFlagNext)
       
   239 			? iDesc[i].iNext : -1;
       
   240 		}
       
   241 	}		
       
   242 	
       
   243 TUint8* DQueue::AllocMem( TUint aSize )
       
   244 	{
       
   245 	TInt r = KErrNone;
       
   246 	
       
   247 #ifdef ENABLE_QEMU_VIRTIO_CLEANUP_BUG_WORKAROUND		
       
   248 	// note this is second part of workaround that deals
       
   249 	// with the issue that memory once assigned to queuebase cannot be 
       
   250 	// changed,
       
   251 	// if queuebase != 0 this is because it was set when the driver
       
   252 	// was loaded previous time
       
   253 
       
   254 	iPhysAddr = (TUint32) iVirtIo.GetQueueBase( iId );
       
   255 	iPhysMemReallyAllocated = (iPhysAddr == 0);
       
   256 	if (iPhysMemReallyAllocated)
       
   257 		{ r = Epoc::AllocPhysicalRam(aSize, iPhysAddr, 0 ); }
       
   258 
       
   259 #endif
       
   260 
       
   261 	if (r!=KErrNone ) 
       
   262 		{ return NULL; }
       
   263 	
       
   264 	r = DPlatChunkHw::New( iChunk, iPhysAddr, aSize, 
       
   265 		EMapAttrSupRw | EMapAttrL2Uncached | EMapAttrL1Uncached );
       
   266 
       
   267 	if (r!=KErrNone ) 
       
   268 		{
       
   269 		if ( iPhysMemReallyAllocated )
       
   270 			{ Epoc::FreePhysicalRam( iPhysAddr, aSize ); }
       
   271 		iChunk->Close( NULL );
       
   272 		return NULL;
       
   273 		}
       
   274 
       
   275 	ASSERT( r == KErrNone );
       
   276 	return reinterpret_cast<TUint8*>( iChunk->LinearAddress() );
       
   277 	}	
       
   278 	
       
   279 TInt DQueue::AllocQueue()
       
   280 	{
       
   281 	iDescSize = iCount * sizeof(TRingDesc);
       
   282 	iAvailSize = sizeof(TRingAvail) + (iCount-1) * sizeof(((TRingAvail*)0)->iRing[0]);
       
   283 	iTokenSize = iCount * sizeof(TTransactionInfo);
       
   284 	TUint usedOffset = Align( iDescSize +  iAvailSize, KVirtIoAlignment );
       
   285 	TUint iUsedSize = sizeof(TRingUsed) + (iCount-1) * sizeof(((TRingUsed*)0)->iRing[0]);
       
   286 	TUint size = usedOffset + iUsedSize;
       
   287 	TUint8* iMemAligned;
       
   288 
       
   289 	iMemAligned = AllocMem( size );
       
   290 	
       
   291 	if (!iMemAligned)
       
   292 		{ return KErrNoMemory; }
       
   293 	
       
   294 	iDesc = reinterpret_cast<TRingDesc*>( iMemAligned );
       
   295 	iAvail = reinterpret_cast<TRingAvail*>( iMemAligned + iDescSize );
       
   296 	iUsed = reinterpret_cast<TRingUsed*>( iMemAligned + usedOffset );
       
   297 	iToken = reinterpret_cast<TTransactionInfo*>( Kern::Alloc( iTokenSize ) );
       
   298 	SYBORG_VIRTIO_DEBUG("DQueue %d, Virt iDesc=%x,iAvail=%x,iToken=%x,iUsed=%x",
       
   299 		iId, iDesc, iAvail, iToken, iUsed );
       
   300 	SYBORG_VIRTIO_DEBUG("DQueue %d, Phys iDesc=%x, iUsed=%x",
       
   301 		iId, PHYS_ADDR(iDesc), PHYS_ADDR(iUsed) );
       
   302 	ASSERT( ((PHYS_ADDR(iUsed)-PHYS_ADDR(iDesc))) == ((TUint32)((TUint8*)iUsed-(TUint8*)iDesc)) );
       
   303 	return KErrNone;
       
   304 	}	
       
   305 	
       
   306 void DQueue::PreInitQueue()
       
   307 	{
       
   308 	memset(iDesc, -1, iDescSize );
       
   309 	memset( ((TUint8*) iAvail) + 4, -1, iDescSize - 4 );
       
   310 	memset( ((TUint8*) iUsed) + 4, -1, iDescSize - 4 );
       
   311 	
       
   312 	iAvail->iFlags = 0; // no notifications from control queue
       
   313 	iUsed->iFlags = 0;
       
   314 	if ( iPhysMemReallyAllocated )
       
   315 		{
       
   316 		iAvail->iIdx = 0;
       
   317 		iUsed->iIdx = 0;
       
   318 		}
       
   319 	}	
       
   320 	
       
   321 void DQueue::PostInitQueue()
       
   322 	{
       
   323 	iFirstEverToSync = iNextAvailToSync = iAvail->iIdx;
       
   324 	iFirstEverToRead = iNextUsedToRead = iUsed->iIdx;
       
   325 	}		
       
   326 
       
   327 
       
   328 } // namespace VirtIo