baseport/syborg/soundsc/virtio_iohandler.cpp
changeset 45 01c1ffcc4fca
child 71 d00bf4f57250
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/baseport/syborg/soundsc/virtio_iohandler.cpp	Thu Mar 04 00:55:21 2010 +0000
@@ -0,0 +1,238 @@
+/*
+* 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_iohandler.h"
+#include "virtio_queue.h"
+#include "virtio_io.h"
+
+namespace VirtIo
+{
+
+DIoHandler::DIoHandler( TAny* aVirtIoBase, TUint aIntNum, TDfcQue* aDfcQue )
+	: iVirtIoBase( aVirtIoBase ), iIntNum( aIntNum ), iDfcQue( aDfcQue ),
+	iDfc( ServeDfc, this, 0 ),
+	iVirtIo( 0 ), iQueueCount( 3 ), iQueue( 0 ),
+	iClientCount( 0 )
+	{
+	SYBORG_VIRTIO_DEBUG("DIoHandler IoBase %x DfcQ %x", iVirtIoBase, iDfcQue);
+	}
+
+TInt DIoHandler::Construct()
+	{
+	TInt err = KErrNone;
+	TUint i;
+
+	SYBORG_VIRTIO_DEBUG("Creating DIo");
+
+	InstallIsr();
+
+	iVirtIo = new DIo(iVirtIoBase);
+	if (iVirtIo == NULL )
+		{ return KErrNoMemory; }
+	iVirtIo->SetStatus( EStatusAcknowledge
+			| EStatusDriverFound );
+
+	iQueue = new DQueue*[iQueueCount];
+	
+	if (iQueue == NULL )
+		{ Wipe(); return KErrNoMemory; }
+	
+	// This is to make Wipe work
+	for ( i = 0; i < iQueueCount; ++i )
+		{ iQueue[i] = NULL; }
+	
+	for ( i = 0; i < iQueueCount; ++i )
+		{
+		SYBORG_VIRTIO_DEBUG("Creating DQueue %d",i);
+		DQueue* q = new DQueue(
+			*iVirtIo, i, iVirtIo->GetQueueCount( i ) );
+		if (!q)
+			{ Wipe(); return KErrNoMemory; }
+		err = q->Construct();
+		if (err != KErrNone)
+			{ Wipe(); return err; }
+		iQueue[i] = q;
+		}
+		
+	iVirtIo->SetStatus(
+		EStatusAcknowledge
+		| EStatusDriverFound
+		| EStatusDriverInitialised );
+
+	iVirtIo->EnableInterrupt( ETrue );
+	Interrupt::Enable(iIntNum);
+	return KErrNone;
+	}
+
+void DIoHandler::Wipe()
+	{
+	if (iQueue)
+		{
+		for ( TUint i = 0; i < iQueueCount; ++i )
+			{
+			if (iQueue[i])
+				{ delete iQueue[i]; }
+			}
+		delete[] iQueue;
+		iQueue = NULL;
+		}
+	if (iVirtIo)
+		{
+		delete iVirtIo;
+		iVirtIo = NULL;
+		}
+	}
+
+DIoHandler::~DIoHandler()
+	{
+
+	Interrupt::Disable(iIntNum);
+	UninstallIsr();
+	iVirtIo->EnableInterrupt( EFalse );
+	iDfc.Cancel();
+	iVirtIo->ClearInteruptStatus();
+
+	WaitForCompletion();
+
+	iVirtIo->SetStatus( EStatusAcknowledge
+		| EStatusDriverFound );
+	iVirtIo->SetStatus( EStatusAcknowledge );
+
+	Wipe();
+	}
+	
+MQueue& DIoHandler::Queue( TUint id )
+	{ return *(iQueue[id]); }	
+
+void DIoHandler::ScheduleCallback()
+	{
+	iDfc.Enque();
+	}
+
+
+// Waits until device processes all pending requests
+// This code should be really handled by each queue individually
+void DIoHandler::WaitForCompletion()
+	{
+	SYBORG_VIRTIO_DEBUG("WaitForCompletion : {");
+
+	TInt st = Kern::PollingWait( &DIoHandler::CheckProcessing, this, 10, 100 );
+
+	ASSERT( st == KErrNone );
+
+	for ( TUint i = 0; i < iQueueCount; ++i )
+		{
+		while ( iQueue[i]->Completed() )
+			{ InterruptHandler(); }
+		}
+
+	SYBORG_VIRTIO_DEBUG("WaitForCompletion : }");
+	}
+
+TBool DIoHandler::CheckProcessing( TAny* aSelf )
+	{
+	DIoHandler* self = reinterpret_cast<DIoHandler*>( aSelf );
+	for ( TUint i = 0; i < self->iQueueCount; ++i )
+		{
+		if (self->iQueue[i]->Processing()!=0)
+			{ return EFalse; }
+		}
+	return ETrue;
+	}
+
+void DIoHandler::InstallIsr()
+	{
+	iDfc.SetDfcQ( iDfcQue );
+	Interrupt::Bind(iIntNum,ServeIsr,this);
+	}
+
+void DIoHandler::UninstallIsr()
+	{
+	Interrupt::Unbind(iIntNum);
+	iDfc.Cancel();
+	}
+
+void DIoHandler::ServeIsr( TAny* aSelf )
+	{
+	DIoHandler* self = reinterpret_cast<DIoHandler*>( aSelf );
+	Interrupt::Clear( self->iIntNum );
+	self->iVirtIo->ClearInteruptStatus();
+	self->iDfc.Add();
+	}
+
+void DIoHandler::ServeDfc( TAny* aSelf )
+	{
+	reinterpret_cast<DIoHandler*>( aSelf )->InterruptHandler();
+	}
+
+	
+// Although the function notifies all clients
+// usually only one of them is an adressee
+// the rest would just check flags/compare numbers and return.
+// If at least one client did some crucial processing 
+// (indicating that by returning EFalse from VirtIoCallback)
+// then NotifyClients returns EFalse as well.
+TBool DIoHandler::NotifyClients( MQueue& aQueue, Token aToken, TUint aBytesTransferred )
+	{
+	TBool r = ETrue;
+	for ( TUint i = 0 ; i < iClientCount; ++i )
+		{
+		r &= iClients[i]->VirtIoCallback( *this, aQueue, aToken, aBytesTransferred );
+		}
+	return r;
+	}
+	
+// Here buffers processed by the device are iterated.
+// After the first serious buffer processing (as indicated by NotifyClients)
+// further buffer processing is Deferred in another DFC callback.
+void DIoHandler::InterruptHandler()
+	{
+	for ( TUint q = 0; q < iQueueCount; ++q )
+		{
+		TUint transferred;
+		TUint cnt = ETrue;
+		do 
+			{
+			Token t = Queue(q).GetBuf(transferred);
+			if (t)
+				{ cnt = NotifyClients( Queue(q), t, transferred); }
+			else 
+				{ break; }
+			} while(cnt);
+		if (!cnt)
+			{
+			ScheduleCallback(); 
+			break;
+			}
+		}
+	}
+
+void DIoHandler::UnregisterClient( MIoCallback* aClient )
+	{
+	ASSERT( iClientCount );
+	TUint i;
+	for ( i = 0; i < iClientCount; ++i)
+		{
+		if (iClients[i] == aClient )
+			{ break; }
+		}
+	ASSERT( i < iClientCount );
+	--iClientCount;
+	// move the rest of the table one slot to the front
+	for ( ; i < iClientCount; ++i) 
+		{ iClients[i] = iClients[i+1]; }
+	}	
+
+} // namespace VirtIo