diff -r 72a7468afdd4 -r 01c1ffcc4fca baseport/syborg/soundsc/virtio_iohandler.cpp --- /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( 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( aSelf ); + Interrupt::Clear( self->iIntNum ); + self->iVirtIo->ClearInteruptStatus(); + self->iDfc.Add(); + } + +void DIoHandler::ServeDfc( TAny* aSelf ) + { + reinterpret_cast( 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