--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/commsfwutils/commsbufs/reference/zerocopy_loopback_driver/pdd.cpp Thu Dec 17 09:22:25 2009 +0200
@@ -0,0 +1,408 @@
+// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "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:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include <kernel/kern_priv.h>
+
+#ifdef SYMBIAN_OLD_EXPORT_LOCATION
+#include <comms-infras/zerocopy_loopback_driver.h>
+#else
+//this header is not exported, needs to be a user include
+#include "zerocopy_loopback_driver.h"
+#endif
+
+#include "device.h"
+
+// Name for PDD, must match LDD name with a '.' and distinguishing name appended
+_LIT(KZeroCopyLoopbackPddName,"ZeroCopyLoopbackDriver.pdd");
+
+
+class TQueue
+ {
+public:
+ DCommsBuf* HeadBuffer() {return &iQueue[iQueueHead];}
+ DCommsBuf* TailBuffer() {return &iQueue[iQueueTail];}
+
+ TBool IsEmpty() const {return iLength == 0;}
+ TBool IsFull() const {return iLength == KLoopbackQueueLen;}
+ TInt MaxLength() const {return KLoopbackQueueLen;}
+ TInt Length() const {return iLength;}
+
+ void Pop()
+ {
+ __ASSERT_ALWAYS(iLength > 0, Kern::Fault("comms loopback", KErrOverflow));
+ iQueueHead++;
+ if(iQueueHead == KLoopbackQueueLen)
+ {
+ iQueueHead = 0;
+ }
+ iLength--;
+ }
+
+ void Push()
+ {
+ __ASSERT_ALWAYS(iLength < KLoopbackQueueLen, Kern::Fault("comms loopback", KErrOverflow));
+ iQueueTail++;
+ if(iQueueTail == KLoopbackQueueLen)
+ {
+ iQueueTail = 0;
+ }
+ iLength++;
+ }
+
+ TQueue() : iQueueHead(0), iQueueTail(0), iLength(0) {}
+
+private:
+ DCommsBuf iQueue[KLoopbackQueueLen];
+ TInt iQueueHead;
+ TInt iQueueTail;
+ TInt iLength;
+ };
+
+
+class DZeroCopyLoopbackDevice : public DZeroCopyLoopback
+ {
+public:
+ DZeroCopyLoopbackDevice(DZeroCopyLoopbackPddFactory* aFactory);
+ ~DZeroCopyLoopbackDevice();
+ TInt DoCreate();
+ // Inherited from DZeroCopyLoopback. These called by the LDD.
+ virtual TInt BufferSize() const;
+ virtual TInt Speed() const;
+ virtual TInt SetSpeed(TInt aSpeed);
+ virtual TInt RequestDataSend();
+ virtual void SendDataCancel();
+ virtual TInt RequestDataReceipt();
+ virtual void ReceiveDataCancel();
+ virtual DCommsBuf* SendBuffer();
+ virtual DCommsBuf* ReceiveBuffer();
+ virtual TBool ReceivedQueueLen();
+ virtual void AdvanceReceiveQueue();
+
+private:
+ void SendDataCallback();
+ void ReceiveDataCallback();
+
+ static void DataRecvCallback(TAny* aSelf);
+ void DoDataRecvCallback();
+
+private:
+ DZeroCopyLoopbackPddFactory* iFactory;
+ TInt iSpeed;
+
+ TQueue iSendQueue;
+ TQueue iReceiveQueue;
+
+ TBool iPendingRead;
+ TDfc iRecvDataDfc;
+ };
+
+
+
+const TInt KZeroCopyLoopbackThreadPriority = 27;
+_LIT(KZeroCopyLoopbackThread,"ZeroCopyLoopbackThread");
+
+/**
+ Standard export function for PDDs. This creates a DPhysicalDevice derived object,
+ in this case, our DZeroCopyLoopbackPddFactory
+*/
+DECLARE_STANDARD_PDD()
+ {
+ return new DZeroCopyLoopbackPddFactory;
+ }
+
+DZeroCopyLoopbackPddFactory::DZeroCopyLoopbackPddFactory()
+ {
+ // Set version number for this device
+ iVersion = RZeroCopyLoopbackDriver::VersionRequired();
+ }
+
+/**
+ Second stage constructor for DPhysicalDevice derived objects.
+ This must at least set a name for the driver object.
+
+ @return KErrNone or standard error code.
+*/
+TInt DZeroCopyLoopbackPddFactory::Install()
+ {
+ // Allocate a kernel thread to run the DFC
+ TInt r = Kern::DynamicDfcQCreate(iDfcQ, KZeroCopyLoopbackThreadPriority, KZeroCopyLoopbackThread);
+ if (r == KErrNone)
+ {
+ r = SetName(&KZeroCopyLoopbackPddName);
+ }
+ return r;
+ }
+
+/**
+ Returns the drivers capabilities. This is not used by the Symbian OS device driver framework
+ but may be useful for the LDD to use.
+
+ @param aDes Descriptor to write capabilities information into
+*/
+void DZeroCopyLoopbackPddFactory::GetCaps(TDes8& aDes) const
+ {
+ // Create a capabilities object
+ DZeroCopyLoopback::TCaps caps;
+ caps.iVersion = iVersion;
+ // Zero the buffer
+ TInt maxLen = aDes.MaxLength();
+ aDes.FillZ(maxLen);
+ // Copy cpabilities
+ TInt size=sizeof(caps);
+ if(size>maxLen)
+ size=maxLen;
+ aDes.Copy((TUint8*)&caps,size);
+ }
+
+/**
+ Called by the kernel's device driver framework to create a Physical Channel.
+ This is called in the context of the user thread (client) which requested the creation of a Logical Channel
+ (E.g. through a call to RBusLogicalChannel::DoCreate)
+ The thread is in a critical section.
+
+ @param aChannel Set to point to the created Physical Channel
+ @param aUnit The unit argument supplied by the client to RBusLogicalChannel::DoCreate
+ @param aInfo The info argument supplied by the client to RBusLogicalChannel::DoCreate
+ @param aVer The version number of the Logical Channel which will use this Physical Channel
+
+ @return KErrNone or standard error code.
+*/
+TInt DZeroCopyLoopbackPddFactory::Create(DBase*& aChannel, TInt aUnit, const TDesC8* aInfo, const TVersion& aVer)
+ {
+ // Ignore the parameters we aren't interested in...
+ (void)aUnit;
+ (void)aInfo;
+ (void)aVer;
+
+ // Create a new physical channel
+ DZeroCopyLoopbackDevice* device = new DZeroCopyLoopbackDevice(this);
+ aChannel=device;
+ if (!device)
+ return KErrNoMemory;
+ return device->DoCreate();
+ }
+
+/**
+ Called by the kernel's device driver framework to check if this PDD is suitable for use with a Logical Channel.
+ This is called in the context of the user thread (client) which requested the creation of a Logical Channel
+ (E.g. through a call to RBusLogicalChannel::DoCreate)
+ The thread is in a critical section.
+
+ @param aUnit The unit argument supplied by the client to RBusLogicalChannel::DoCreate
+ @param aInfo The info argument supplied by the client to RBusLogicalChannel::DoCreate
+ @param aVer The version number of the Logical Channel which will use this Physical Channel
+
+ @return KErrNone or standard error code.
+*/
+TInt DZeroCopyLoopbackPddFactory::Validate(TInt aUnit, const TDesC8* aInfo, const TVersion& aVer)
+ {
+ // Check version numbers
+ if ((!Kern::QueryVersionSupported(iVersion,aVer)) || (!Kern::QueryVersionSupported(aVer,TVersion(EMinimumLddMajorVersion,EMinimumLddMinorVersion,EMinimumLddBuild))))
+ return KErrNotSupported;
+
+ // We don't support units
+ if (aUnit != -1)
+ return KErrNotSupported;
+
+ // Ignore extra info, (this could be used for validation purposes)
+ // Note, aInof is a pointer to a descriptor in user memory, therefore safe methods should
+ // be used for reading its contents. E.g. using Kern::KUDesGet()
+ (void)aInfo;
+
+ // OK
+ return KErrNone;
+ }
+
+DZeroCopyLoopbackPddFactory::~DZeroCopyLoopbackPddFactory()
+ {
+ if (iDfcQ)
+ iDfcQ->Destroy();
+ }
+
+DZeroCopyLoopbackDevice::DZeroCopyLoopbackDevice(DZeroCopyLoopbackPddFactory* aFactory)
+ : iFactory(aFactory),
+ iSpeed(100000), // 100000us (100ms) per byte
+ iSendQueue(),
+ iReceiveQueue(),
+ iRecvDataDfc(DataRecvCallback, this, aFactory->iDfcQ, 1)
+
+ {
+ }
+
+DZeroCopyLoopbackDevice::~DZeroCopyLoopbackDevice()
+ {
+ // Empty the queues
+ // This means freeing up any DCommsBufs left inside
+ while(!iSendQueue.IsEmpty())
+ {
+ iSendQueue.HeadBuffer()->Free();
+ iSendQueue.Pop();
+ }
+ while(!iReceiveQueue.IsEmpty())
+ {
+ iSendQueue.HeadBuffer()->Free();
+ iReceiveQueue.Pop();
+ }
+
+ // Driver no longer using hardware resources
+ NKern::LockedDec(iFactory->iHardwareInUse);
+ }
+
+TInt DZeroCopyLoopbackDevice::DoCreate()
+ {
+ // Claim the hardware resources by incrementing iHardwareInUse.
+ // Must do this before any other failure can happen in this function so that
+ // the destructor can safely decrement iHardwareInUse.
+ //
+ // This method of ensuring hardware is only in use by one driver at a time
+ // wouldn't be needed if the driver claimed real hardware resources which
+ // could only be used once. E.g. binding to an interrupt.
+ if(NKern::LockedInc(iFactory->iHardwareInUse))
+ return KErrInUse;
+
+ // Other setup goes here
+
+ return KErrNone;
+ }
+
+TInt DZeroCopyLoopbackDevice::BufferSize() const
+ {
+ return 1500;
+ }
+
+TInt DZeroCopyLoopbackDevice::Speed() const
+ {
+ return iSpeed;
+ }
+
+TInt DZeroCopyLoopbackDevice::SetSpeed(TInt aSpeed)
+ {
+ if(aSpeed<=0)
+ return KErrArgument;
+ iSpeed = aSpeed;
+ return KErrNone;
+ }
+
+TInt DZeroCopyLoopbackDevice::RequestDataSend()
+ {
+ // Push our send buffer in to the queue
+ iSendQueue.Push();
+
+ // Trigger reception
+ NKern::Lock();
+ SendDataCallback();
+ NKern::Unlock();
+
+ return KErrNone;
+ }
+
+void DZeroCopyLoopbackDevice::SendDataCancel()
+ {
+ }
+
+void DZeroCopyLoopbackDevice::SendDataCallback()
+ {
+ // Tell self new data is coming
+ iRecvDataDfc.Add();
+
+ // Tell LDD we've done sending
+ iLdd->SendDataComplete(KErrNone);
+ }
+
+TInt DZeroCopyLoopbackDevice::RequestDataReceipt()
+ {
+ iPendingRead = ETrue;
+ if(!(iReceiveQueue.IsEmpty() && iSendQueue.IsEmpty()))
+ {
+ NKern::Lock();
+ ReceiveDataCallback();
+ NKern::Unlock();
+ }
+ return KErrNone;
+ }
+
+void DZeroCopyLoopbackDevice::ReceiveDataCancel()
+ {
+ }
+
+void DZeroCopyLoopbackDevice::ReceiveDataCallback()
+ {
+ // Copy buffer from send queue (it's like our receive hardware) to receive queue
+ DCommsBuf* srcBuf = iSendQueue.HeadBuffer();
+ if(!iReceiveQueue.IsFull() && !iSendQueue.IsEmpty())
+ {
+ // Alloc a new buffer in to which we will copy the received (sent) buffer (mimicking DMA in the non zerocopy case)
+ DCommsBuf newBuf;
+ TInt result = iLdd->Pond().Alloc(newBuf);
+ if(result == KErrNone)
+ {
+ // Copy our buffer and set the new buffer's properties
+ TInt srcPayloadLength = srcBuf->Length();
+ newBuf.SetOffset(0);
+ newBuf.SetLength(srcPayloadLength);
+
+ // Do the copy
+ TUint8* srcPayloadPtr = srcBuf->Ptr();
+ TUint8* destPayloadPtr = newBuf.Ptr();
+ memcpy(destPayloadPtr, srcPayloadPtr, srcPayloadLength);
+
+ // Put the new buffer in the receive queue
+ DCommsBuf* destBuf = iReceiveQueue.TailBuffer();
+ *destBuf = newBuf;
+ iReceiveQueue.Push();
+
+ // Drop the incoming buffer
+ srcBuf->Free();
+
+ // Step the queue
+ iSendQueue.Pop();
+ }
+
+ // We can now complete any outstanding request for received data - if indeed we managed to alloc a receive buffer
+ if(iPendingRead && !iReceiveQueue.IsEmpty())
+ {
+ iPendingRead = FALSE;
+
+ // Tell LDD we're done
+ iLdd->ReceiveDataComplete(KErrNone);
+ }
+ }
+ }
+
+DCommsBuf* DZeroCopyLoopbackDevice::SendBuffer()
+ {
+ return iSendQueue.TailBuffer();
+ }
+
+DCommsBuf* DZeroCopyLoopbackDevice::ReceiveBuffer()
+ {
+ return iReceiveQueue.HeadBuffer();
+ }
+
+void DZeroCopyLoopbackDevice::AdvanceReceiveQueue()
+ {
+ iReceiveQueue.Pop();
+ }
+
+TInt DZeroCopyLoopbackDevice::ReceivedQueueLen()
+ {
+ return iReceiveQueue.Length();
+ }
+
+
+void DZeroCopyLoopbackDevice::DataRecvCallback(TAny* aSelf)
+ {
+ ((DZeroCopyLoopbackDevice*)aSelf)->ReceiveDataCallback();
+ }