--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/pipe/dpipe.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,2194 @@
+// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// 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:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+
+#include <kernel/kern_priv.h>
+#include "dpipe.h"
+
+//_LIT(KPipePanicCategory,"PipePanic");
+const TInt KPipeGranularity = 8;
+
+DECLARE_STANDARD_LDD()
+/**
+Standard export function for LDDs. This creates a DLogicalDevice derived
+object, in this case our DPipeDevice
+*/
+ {
+ return new DPipeDevice;
+ }
+
+DPipeDevice::DPipeDevice()
+/**
+DPipeDevice Constructor has minimal implementation such setting the version number
+Indicate the use of unit number
+
+@param None
+
+@return None
+*/
+ {
+ iCount = 0;
+ iIdindex = 0;
+ iAllocated = 0;
+ iVersion = RPipe::VersionRequired();
+ }
+
+
+DPipeDevice::~DPipeDevice()
+ {
+ // Delete the existing pipes
+ for(TInt count = 0; count<iCount; count++)
+ {
+ DPipe* pipe=iDpipes[count];
+ pipe->Wait();
+ pipe->CloseAll();
+ delete pipe;
+ }
+ Kern::Free(iDpipes);
+ iMutex->Close(NULL);
+ }
+
+
+TInt DPipeDevice::Install()
+/**
+Second stage constructor and at least set a name for the
+driver object. Inherited from DLogicalDevice. This must at least set a name
+for the driver object.
+
+@param None
+
+@return KErrNone If successful, otherwise one of the system wide error codes.
+*/
+ {
+ _LIT(KMutexName,"PipeDeviceMutex");
+ TInt err = Kern::MutexCreate(iMutex, KMutexName, KMutexOrdGeneral1);
+ if (err)
+ {
+ return err;
+ }
+
+ return SetName(&RPipe::Name());
+ }
+
+
+void DPipeDevice::GetCaps(TDes8& aDes) const
+/**
+Returns the driver capabilities. Called in the response to
+an RPipe::GetCaps() request
+
+@param aDes Descriptor into which capabilities information
+ is to be written
+@return None
+*/
+ {
+ // Write it back to user memory
+ TVersion version;
+ version = iVersion;
+ Kern::InfoCopy(aDes,(TUint8*)&version, sizeof(version));
+ }
+
+
+TInt DPipeDevice::Create(DLogicalChannelBase*& aChannel)
+/**
+Called by the kernel's device driver framework to create a Logical Channel.
+This is called in the context of the user thread (client) which requested the
+creation of the Logical Channel.
+
+ @param aChannel Set to point to the created logical channel
+
+ @return KErrNone If successful, otherwise system one of the other
+ wide error codes.
+ */
+ {
+ aChannel = new DPipeChannel;
+ if (!aChannel)
+ return KErrNoMemory;
+ return KErrNone;
+ }
+
+
+TInt DPipeDevice::CreatePipe(const TDesC& aName, TInt aSize, DPipe*& aPipe, TAny* aCap)
+/**
+Called by DPipeChannel instance to create named DPipe object and
+associate itself with the newly created named DPipe instance.
+
+@param aName name need to be attached to the newly created DPipe object.
+@param aSize size of the DPipe object.
+@param aPipe Pointer to DPipe, If successful set the pointer to newly created DPipe instance else NULL
+@param aCap Pointer to TSecuritypolicy passed as void pointer
+
+@return KErrNone If successful, otherwise one of the other system wide error code
+@pre Calling thread must be in a critical section.
+@pre Mutex must be held
+*/
+ {
+ __ASSERT_MUTEX(iMutex);
+ __KTRACE_OPT(KPIPE, Kern::Printf(">DPipeDevice::CreatePipe"));
+ TInt err = KErrNone;
+ DPipe** pS = iDpipes;
+ DPipe** pE = pS + iCount;
+ while(pS < pE)
+ {
+ DPipe* pO = *pS++;
+ if((pO->MatchName(&aName)))
+ {
+ err = KErrAlreadyExists;
+ break;
+ }
+ }
+ if(err == KErrNone)
+ {
+ DPipe* pipe = DPipe::CreatePipe(aName, aSize, aCap);
+ if(pipe)
+ {
+ err = AddPipe(pipe);
+ if(err!= KErrNone)
+ {
+ delete pipe;
+ }
+ else
+ {
+ aPipe = pipe;
+ }
+ }
+ else
+ {
+ err = KErrNoMemory;
+ }
+ }
+ __KTRACE_OPT(KPIPE, Kern::Printf("<DPipeDevice::CreatePipe ret=%d", err));
+ return err;
+ }
+
+
+DPipe* DPipeDevice::CreatePipe(TInt aSize)
+/**
+Called by DPipeChannel instance to create un-named DPipe instance and
+associate itself with the newly created un-named DPipe instance.
+
+@param aSize size of the DPipe object.
+
+@return DPipe* If successful, otherwise NULL
+@pre Mutex must be held
+@pre In critical section
+*/
+ {
+ __ASSERT_CRITICAL;
+ __ASSERT_MUTEX(iMutex);
+ TKName aName;
+ DPipe* pipe = DPipe::CreatePipe(aName, aSize);
+ if(!pipe)
+ {
+ return NULL;
+ }
+
+ TInt r = AddPipe(pipe);
+ if (r != KErrNone)
+ {
+ delete pipe;
+ return NULL;
+ }
+ return pipe;
+ }
+
+
+
+TInt DPipeDevice::AddPipe(DPipe* aObj)
+/**
+Add an instance of Dpipe to the array.
+@param aObj Pointer to DPipe object
+@return KErrNone If the call is successful otherwise one of the other
+ system wide error code.
+
+@pre Calling thread must be in a critical section.
+@pre Mutex to be held
+*/
+ {
+ __ASSERT_CRITICAL; //otherwise iDPipes and iCount could go out of sync
+ __ASSERT_MUTEX(iMutex);
+ // store the current instance to the array
+ if(iCount == iAllocated)
+ {
+ TInt newAlloc = iAllocated + KPipeGranularity;
+ TInt r = Kern::SafeReAlloc((TAny*&)iDpipes, iCount * sizeof(DPipe*), newAlloc * sizeof(DPipe*));
+ if (r!= KErrNone)
+ {
+ return r;
+ }
+ iAllocated = newAlloc;
+ }
+ TInt id = GenerateId();
+ aObj->SetId(id);
+ iDpipes[iCount++]= aObj;
+
+ __KTRACE_OPT(KPIPE, Kern::Printf("DPipeDevice::AddPipe Pipe added ID=%d", id));
+ return KErrNone;
+ }
+
+
+
+void DPipeDevice::RemovePipe(DPipe** aObj)
+/**
+Remove an instance of DPipe from the array
+
+@param Pointer to Dpipe Array
+
+@return None
+
+@pre Calling thread must not be in a critical section.
+@pre Mutex to be held
+*/
+ {
+ __ASSERT_MUTEX(iMutex);
+ __ASSERT_CRITICAL; //we don't want to leave the array inconsistant
+
+ DPipe** pE = (iDpipes + iCount) - 1;
+ if(aObj<pE)
+ {
+ //bump along array elements to close the gap
+ wordmove((TAny*)aObj, (TAny*)(aObj+1), TInt(pE)- TInt(aObj));
+ }
+ --iCount;
+ if(iCount % KPipeGranularity == 0)
+ {
+ Kern::SafeReAlloc((TAny*&)iDpipes, iAllocated*sizeof(DPipe*), iCount* sizeof(DPipe*));
+ iAllocated = iCount;
+ }
+ }
+
+
+DPipe* DPipeDevice::FindNamedPipe(const TDesC* aName)
+/**
+Called by the DPipeChannel to check if a named DPipe instance exist with a name
+as specified by aName parameter.
+
+@param aName The name of the DPipe instance to search for.
+
+@return DPipe* If successful, otherwise NULL
+
+@pre Device mutex to be held
+*/
+ {
+ __ASSERT_MUTEX(iMutex);
+ DPipe** pS = iDpipes;
+ DPipe** pE = pS + iCount;
+
+ while(pS < pE)
+ {
+ DPipe* pO = *pS++;
+ if(pO->MatchName(aName))
+ {
+ return pO;
+ }
+ }
+ return NULL;
+ }
+
+DPipe* DPipeDevice::FindUnnamedPipe(const TInt aId)
+/**
+Called by the DPipeChannel to check if an un-named DPipe instance exist with an ID
+as specified by aId parameter.
+
+@param aId The ID of the DPipe instance to search for.
+
+@return DPipe* If successful, otherwise NULL
+@pre Device mutex to be held
+*/
+ {
+ __ASSERT_MUTEX(iMutex);
+ DPipe** pS = iDpipes;
+ DPipe** pE = pS + iCount;
+ while(pS < pE)
+ {
+ DPipe* pO = *pS++;
+ if(pO->MatchId(aId))
+ {
+ return pO;
+ }
+ }
+ return NULL;
+ }
+
+TInt DPipeDevice::Destroy(const TDesC* aName)
+/**
+This method is called to destroy a named DPipe instance. The caller needs to have
+sufficient capabilities to delete a named pipe. This method will fail if there are
+any handles still open on the pipe.
+
+@param aName Name of the DPipe instance to be deleted.
+
+@return KErrNone If successful, otherwise one of the other system wide error.
+
+*/
+ {
+ TAutoWait<DMutex> autoMutex(*iMutex);
+ DPipe** pS = iDpipes;
+ DPipe** pE = pS + iCount;
+ TInt err = KErrNotFound;
+ TInt count = 0;
+ while(pS < pE)
+ {
+ DPipe** pO = pS++;
+ DPipe* pipe = *pO;
+ if(((*pO)->MatchName(aName)))
+ {
+ //! Check capability
+ if(pipe->GetCap())
+ {
+ if(!(pipe->GetCap()->CheckPolicy(&Kern::CurrentThread())))
+ {
+ err = KErrPermissionDenied;
+ break;
+ }
+ }
+ // Check if any handles still opened on the pipe.
+ pipe->Wait();
+ if (!pipe->IsPipeClosed())
+ {
+ err = KErrInUse;
+ pipe->Signal(); //need to signal if we won't be destroying pipe
+ break;
+ }
+ __KTRACE_OPT(KPIPE, Kern::Printf("DPipeDevice::Destroy remove ID=%d", pipe->OpenId()));
+ delete iDpipes[count];
+ RemovePipe(pO);
+ err = KErrNone;
+ break;
+ }
+ count ++;
+ }
+ return err;
+ }
+
+
+TInt DPipeDevice::Close(TInt aId)
+/**
+This method is called to close both named and un-named DPipe. In case of un-named DPipe
+if there is no further reference of a DPipeChannel exist, the corresponding un-named DPipe
+will be deleted.
+
+@param aId ID of the pipe that need to be closed.
+
+@return KErrNone If successful otherwise one of the other system wide error.
+
+*/
+ {
+ TAutoWait<DMutex> autoMutex(*iMutex);
+ DPipe** pS = iDpipes;
+ DPipe** pE = pS + iCount;
+ TInt err = KErrNotFound;
+ while(pS < pE)
+ {
+ DPipe** pO = pS++;
+ DPipe* pipe = *pO;
+ if(pipe->MatchId(aId))
+ {
+ __KTRACE_OPT(KPIPE, Kern::Printf("DPipeDevice::Close found ID=%d", pipe->OpenId()));
+ //even if we can't delete the pipe, we have
+ //found it so don't return KErrNotFound
+ err = KErrNone;
+
+ pipe->Wait();
+
+ // we can only delete an unamed pipe with both ends closed
+ if(!pipe->IsNamedPipe() && pipe->IsPipeClosed())
+ {
+ __KTRACE_OPT(KPIPE, Kern::Printf("DPipeDevice::Close remove ID=%d", pipe->OpenId()));
+ delete pipe;
+ RemovePipe(pO);
+ break;
+ }
+ pipe->Signal();
+
+ }
+ }
+ return err;
+ }
+
+
+
+TInt DPipeDevice::GenerateId()
+/**
+Generate a ID and store for a Named pipe while creating.
+
+@param None
+@return TInt ID for the name pipe
+
+@pre Mutex to be held
+
+*/
+ {
+ __ASSERT_MUTEX(iMutex);
+ iIdindex++;
+ return (KIdBase + iIdindex);
+ }
+
+
+DPipeChannel::DPipeChannel()
+ :iClientRequest(NULL), iData(NULL), iChannelType(RPipe::EChannelUnset)
+/**
+Constructor
+*/
+ {
+ }
+
+
+DPipeChannel::~DPipeChannel()
+/**
+Destructor
+*/
+ {
+ CloseHandle();
+
+ Kern::DestroyClientRequest(iClientRequest); //null ptr is safe
+ }
+
+
+
+TInt DPipeChannel::RequestUserHandle (DThread* aThread, TOwnerType aType)
+/**
+Inherited from DObject. This method is called when a user thread requests
+a handle to this channel. Minimal implantation here is capability check
+
+@param aThread DThread instance reference that requests a handle to this channel.
+@param aType Ownership type for the handle.
+
+@return KErrNone If successful otherwise one the system wide error.
+*/
+ {
+ (void)aThread;
+ (void)aType;
+ return KErrNone;
+ }
+
+
+
+TInt DPipeChannel::DoCreate (TInt aUnit, const TDesC8* aInfo, const TVersion& aVer)
+/**
+Inherited from DLogicalChannelBase class. This method represents the second stage
+constructor called by the kernel's device driver framework. This is called in the
+context of the user thread (client) which requested the creation of the Logical
+Channel. The thread is in critical section.
+
+@param aUnit The unit argument supplied by the client
+@param aInfo The info argument supplied by the client
+@param aVer The version argument supplied by the client
+
+@return KErrNone If successful, otherwise one of the other system wide error codes.
+*/
+ {
+ (void)aInfo;
+ (void)aUnit;
+
+ // Check version
+ if (!Kern::QueryVersionSupported(RPipe::VersionRequired(),aVer))
+ return KErrNotSupported;
+
+ TInt r = Kern::CreateClientRequest(iClientRequest);
+ if(r != KErrNone)
+ {
+ return r;
+ }
+
+ // Done
+ return KErrNone;
+ }
+
+
+TInt DPipeChannel::Request(TInt aReqNo, TAny* a1, TAny* a2)
+/**
+Called by the Device driver framework upon user request. Stores the
+Thread pointer under whose context this function is called.
+
+@param aFunction A number identifying the message type
+@param a1 A 32-bit Value passed by the user
+@param a2 A 32-bit Value passed by the user
+
+@return KErrNone If successful, otherwise one of the system wide error code
+
+*/
+ {
+ TInt err = KErrNone;
+
+ DATAPAGING_TEST
+ (
+ err = Kern::HalFunction(EHalGroupVM, EVMHalFlushCache, 0, 0);
+ if(err != KErrNone)
+ {
+ return err;
+ }
+ )
+
+ if(aReqNo == KMaxTInt)
+ {
+ CancelRequest((TInt)a1);
+ return err;
+ }
+ if(aReqNo < 0)
+ {
+ // DoRequest
+ TAny *array[2] = {0,0};
+ TRequestStatus * pStat = (TRequestStatus*)a1;
+ kumemget32(&array[0], a2, 2*sizeof(TAny*));
+ err = DoRequest(~aReqNo, pStat, array[0], array[1]);
+ if(err!= KErrNone)
+ Kern::RequestComplete(pStat, err);
+
+ }
+ else
+ {
+ // DoControl
+ err = DoControl(aReqNo, a1, a2);
+ }
+ return err;
+ }
+
+
+TInt DPipeChannel::DoControl(TInt aFunction, TAny* a1, TAny* a2)
+/**
+Processes Synchronous 'control' requests. This function is called to service
+any synchronous calls through the user side RPipe handle.
+
+@param aFunction A number identifying the message type
+@param a1 A 32-bit Value passed by the user
+@param a2 A 32-bit Value passed by the user
+
+@return KErrNone If the call is successful, otherwise one of the other
+ system wide error
+*/
+ {
+ TInt aSize = 0;
+ TInt aId = 0;
+
+ switch(aFunction)
+ {
+ case RPipe::EDefineNamedPipe:
+ return PipeCreate(a1, a2);
+
+ case RPipe::EOpenToReadNamedPipe:
+ return PipeOpen((const TDesC*)a1, RPipe::EReadChannel);
+
+ case RPipe::EOpenToWriteNamedPipe:
+ return PipeOpen((const TDesC*)a1, RPipe::EWriteChannel);
+
+ case RPipe::EOpenToWriteButFailOnNoReaderNamedPipe:
+ return OpenOnReader((const TDesC*)a1);
+
+ case RPipe::EDestroyNamedPipe:
+ return PipeDestroy((const TDesC*)a1);
+
+ case RPipe::ECreateUnNamedPipe:
+ kumemget((TAny*)&aSize, a1, sizeof(TInt));
+ return PipeCreate( aSize);
+
+ case RPipe::EOpenUnNamedPipe:
+ kumemget((TAny*)&aId, a1, sizeof(TInt));
+ return PipeOpen(aId);
+
+ case RPipe::ERead:
+ kumemget((TAny*)&aSize, a2, sizeof(TInt));
+ return Read (a1, aSize);
+
+ case RPipe::EWrite:
+ kumemget((TAny*)&aSize, a2, sizeof(TInt));
+ return Write (a1, aSize);
+
+ case RPipe::ESize:
+ return Size();
+
+ case RPipe::EDataAvailableCount:
+ {
+ TAutoWait<DMutex> autoMutex(iData->Mutex());
+ return iData->AvailableDataCount();
+ }
+
+ case RPipe::EFlushPipe:
+ Flush();
+ return KErrNone;
+
+ case RPipe::EGetPipeInfo:
+ umemput(a1,(TAny*)&iChannelType, sizeof(TInt));
+ aSize = Size();
+ umemput(a2,(TAny*)&aSize, sizeof(TInt));
+ return KErrNone;
+
+
+ default:
+ return KErrNotSupported;
+
+ }
+
+ }
+
+
+TInt DPipeChannel::DoRequest(TInt aReqNo, TRequestStatus* aStatus, TAny* a1, TAny* a2)
+/**
+Processes Asynchronous requests This function is called to service
+any asynchronous calls through the user side RPipe handle.
+
+@param aFunction A number identifying the message type
+@param aStatus Status request to be completed.
+@param a1 A 32-bit Value passed by the user
+@param a2 A 32-bit Value passed by the user
+
+@return KErrNone If the call is successful, else one of the system wide error
+*/
+ {
+ (void)a2;
+ TInt aSize = 0;
+ TInt aChoice = 0;
+
+ switch(aReqNo)
+ {
+ case RPipe::EDataAvailable:
+ return NotifyDataAvailable(aStatus, ETrue);
+
+ case RPipe::ESpaceAvailable:
+ umemget(&aSize, a1, sizeof(aSize));
+ return NotifySpaceAvailable(aSize, aStatus, ETrue);
+
+ case RPipe::EWaitNotification:
+ // a2 == RPipe::EWaitForReader is for WaitForReader.
+ // a2 == RPipe::EWaitForWriter is for WaitForWriter.
+ umemget(&aChoice, a2, sizeof(aChoice));
+ return WaitNotification(aStatus, a1, aChoice);
+
+ case RPipe::EReadBlocking:
+ {
+ return NotifyDataAvailable(aStatus, EFalse);
+ }
+
+ case RPipe::EWriteBlocking:
+ {
+ umemget(&aSize, a1, sizeof(aSize));
+ return NotifySpaceAvailable(aSize, aStatus, EFalse);
+ }
+ default:
+ return KErrNotSupported;
+ }
+ }
+
+
+
+TInt DPipeChannel::PipeCreate(TAny* a1, TAny* a2)
+/**
+Creates named pipes with the specified name and size. It calls Pipe Device
+object to create the pipe and obtained the pointer to it. The pointer is then
+stored in its iData member data.
+@param a1 Pointer to TPipeInfo class
+
+@param a2 Pointer to TSecurityPolicy class
+
+@return KErrNone If successful, otherwise one of the other system wide error code.
+*/
+ {
+ if(iData)
+ {
+ //this channel already has a pipe
+ return KErrInUse;
+ }
+
+ // The following code safely gets the 3 arguments into kernel memory.
+ // (The user side API is badly designed,)
+ RPipe::TPipeInfo& info = (*(RPipe::TPipeInfoBuf*)a1)(); // reference to user side 'TPipeInfo'
+ TInt size;
+ kumemget(&size,&info.isize,sizeof(size));
+ TKName name;
+ Kern::KUDesGet(name,info.iName);
+ TSecurityPolicy* securityPolicy = 0;
+ TSecurityPolicy securityPolicyBuffer;
+ if(a2)
+ {
+ kumemget(&securityPolicyBuffer,a2,sizeof(securityPolicyBuffer));
+ securityPolicy = &securityPolicyBuffer;
+ }
+
+ DPipe * pipe = NULL;
+ DPipeDevice& device = *static_cast<DPipeDevice*>(iDevice);
+
+ //must wait on device since after creation
+ //the pipe becomes globably findable
+ //and destroyable
+ TAutoWait<DMutex> outerAutoMutex(device.Mutex());
+
+ TInt err = ((DPipeDevice*)iDevice)->CreatePipe(name, size, pipe, securityPolicy);
+ if(err!= KErrNone)
+ {
+ return err;
+ }
+
+ TAutoWait<DMutex> innerAutoMutex(pipe->Mutex());
+ pipe->SetReadEnd(this);
+ iData = pipe;
+ iChannelType = RPipe::EReadChannel;
+ return err;
+ }
+
+
+TInt DPipeChannel::PipeCreate(const TInt aSize)
+/**
+Creates unnamed pipes with the specified Id and size. It calls Pipe Device
+object to create the pipe and obtained the pointer to it. The pointer is then
+stored in its iData member data. Marked the current channel as read end.
+
+@param aSize Size of the unnamed pipe to be created.
+
+@return Handle ID if successful, otherwise one of the other system wide error code.
+*/
+ {
+ if(iData)
+ {
+ //this channel already has a pipe
+ return KErrInUse;
+ }
+
+ DPipeDevice& device = *static_cast<DPipeDevice*>(iDevice);
+
+ TAutoWait<DMutex> outerAutoMutex(device.Mutex());
+
+ DPipe* pipe = device.CreatePipe(aSize);
+ if(pipe == NULL)
+ {
+ return KErrNoMemory;
+ }
+
+ TAutoWait<DMutex> innerAutoMutex(pipe->Mutex());
+
+ pipe->SetReadEnd(this);
+ iData = pipe;
+ iChannelType = RPipe::EReadChannel;
+
+ return iData->OpenId();
+ }
+
+
+TInt DPipeChannel::OpenOnReader(const TDesC* aName)
+/**
+Opens a named pipe identified by the name parameter. It calls Pipe Device object
+to open the Pipe identified by the name and obtained the pointer to the pipe. The
+pointer is them stored in its iData member data. Marked the current channel as write
+end.
+@param aName The name of the pipe to be opened.
+
+@return KErrNone If successful, otherwise one of the other system wide error code.
+*/
+ {
+ if(iData)
+ {
+ //this channel already has a pipe
+ return KErrInUse;
+ }
+
+ TKName PName;
+ Kern::KUDesGet(PName, *aName);
+
+ DPipeDevice& device = *static_cast<DPipeDevice*>(iDevice);
+
+ //need to hold the device mutex to
+ //prevent the pipe getting deleted before we can call
+ //SetWriteEnd
+ TAutoWait<DMutex> outerAutoMutex(device.Mutex());
+ DPipe* pipe = device.FindNamedPipe(&PName);
+
+ if(pipe == NULL)
+ {
+ return KErrNotFound;
+ }
+
+ TAutoWait<DMutex> innerAutoMutex(pipe->Mutex());
+ if (!pipe->IsReadEndOpened())
+ {
+ return KErrNotReady;
+ }
+
+ iData = pipe;
+
+ if(!CheckCap())
+ {
+ iData = NULL;
+ return KErrPermissionDenied;
+ }
+
+ if(pipe->IsWriteEndOpened())
+ {
+ iData = NULL;
+ return KErrInUse;
+ }
+
+ iData->SetWriteEnd(this);
+ iChannelType = RPipe::EWriteChannel;
+ return KErrNone;
+ }
+
+
+TInt DPipeChannel::PipeDestroy(const TDesC* aName)
+/**
+Destroys the named pipe.
+@param aName Name of the Kernel pipe to be destroyed.
+
+@return KErrNone If the pipe is successfully destroyed, otherwise one of the
+ other system wide error codes
+*/
+ {
+ TKName PName;
+ Kern::KUDesGet(PName, *aName);
+ return ((DPipeDevice*)iDevice)->Destroy(&PName);
+ }
+
+TInt DPipeChannel::PipeOpen(const TInt aId)
+/**
+Opens a unnamed pipe identified by the specified id. It calls Pipe Device object
+to open a unnamed pipe identified by the specified id and obtain the pointer to the
+pipe. The pipe reference is then stored in its iData member data and marked the
+current channel as write end.
+
+@param aId Id of the unnamed pipe to be opened.
+
+@return KErrNone If successful, otherwise one of the system wide error code.
+*/
+ {
+ if(iData)
+ {
+ //this channel already has a pipe
+ return KErrInUse;
+ }
+
+ DPipeDevice& device = *static_cast<DPipeDevice*>(iDevice);
+ TAutoWait<DMutex> outerAutoMutex(device.Mutex());
+
+ DPipe* pipe = device.FindUnnamedPipe(aId);
+ if(pipe == NULL)
+ {
+ return KErrNotFound;
+ }
+
+ TAutoWait<DMutex> innerAutoMutex(pipe->Mutex());
+ if (pipe->IsWriteEndOpened() )
+ {
+ return KErrInUse;
+ }
+
+ pipe->SetWriteEnd(this);
+
+ iChannelType = RPipe::EWriteChannel;
+ iData = pipe;
+
+ return KErrNone;
+ }
+
+
+TInt DPipeChannel::PipeOpen(const TDesC* aName, RPipe::TChannelType aType)
+/**
+This function will be called under DoControl();
+Attempts to open the pipe for reading (iReadEnd) or writing (iWriteEnd)
+@param aName Name of the pipe to be opened
+
+@param aType Type of operation to be performed.
+
+@return KErrNone Pipe successfully created, otherwise one of the other system wide
+ error code
+*/
+ {
+ if(iData)
+ {
+ //this channel already has a pipe
+ return KErrInUse;
+ }
+
+ TKName PName;
+ Kern::KUDesGet(PName, *aName);
+
+ DPipeDevice& device = *static_cast<DPipeDevice*>(iDevice);
+
+ TAutoWait<DMutex> outerAutoMutex(device.Mutex());
+
+ DPipe* pipe = device.FindNamedPipe(&PName);
+ if(pipe == NULL)
+ {
+ return KErrNotFound;
+ }
+
+
+ TAutoWait<DMutex> innerAutoMutex(pipe->Mutex());
+ iData = pipe;
+ //! Check capabilitity if applicalble
+ if(!CheckCap())
+ {
+ iData = NULL;
+ return KErrPermissionDenied;
+ }
+
+ // Check if the pipe is already opened.
+ if(aType == RPipe::EReadChannel)
+ {
+ if(iData->IsReadEndOpened())
+ {
+ iData = NULL;
+ return KErrInUse;
+ }
+ iData->SetReadEnd(this);
+ }
+ else
+ {
+ if(iData->IsWriteEndOpened())
+ {
+ iData = NULL;
+ return KErrInUse;
+ }
+ iData->SetWriteEnd(this);
+ }
+
+ iChannelType = aType;
+
+ return KErrNone;
+ }
+
+
+
+TBool DPipeChannel::CheckCap()
+/**
+Check if Security policy is installed, if so, checks if the current thread
+has required capabilities
+
+@param None
+
+@return TBool ETrue if The current thread has required capabilities and also if
+ no capabilities is installed, otherwise EFlase.
+
+*/
+ {
+ //iData->GetCap is always true
+ if(iData->GetCap())
+ return iData->GetCap()->CheckPolicy(&Kern::CurrentThread());
+ else
+ return ETrue;
+ }
+
+
+
+TInt DPipeChannel::Read (TAny* aBuff, TInt aSize)
+/**
+Synchronous, non-blocking read operation. If the pipe is empty it will
+return immediately with KErrUnderflow. A successful DPipe::Read() operation
+will free up more space in the pipe. If a request status object has been registered
+for Space Available notification, it will complete. Note that there is no
+guarantee that the amount of space freed up in the pipe will be sufficient
+for the next DPipe::Write() operation.
+
+@param aBuff Buffer from which data need to be read
+
+@param aSize Size of the data to be read
+
+@return:>0 Amount of data read in octets.
+ KErrArgument Invalid Length Amount of data to be read is invalid (e.g. negative)
+ KErrNotReady If the write end is closed,
+ otherwise one of the other system wide error code
+*/
+ {
+
+ if( iChannelType != RPipe::EReadChannel)
+ return KErrAccessDenied;
+
+
+ TAutoWait<DMutex> outerAutoMutex(*iData->iReadMutex);
+ TAutoWait<DMutex> innerAutoMutex(iData->Mutex());
+ //iData->Wait();
+ if(!iData->IsWriteEndOpened() && iData->IsBufferEmpty())
+ {
+ //it is ok to read from a broken pipe provided there is data in it
+ return KErrNotReady;
+ }
+
+ return iData->Read(aBuff, aSize);
+ }
+
+
+TInt DPipeChannel::Write (TAny* aBuff, TInt aSize)
+/**
+Synchronous, non-blocking write operation. If the pipe is full it will
+return immediately with KErrOverflow. A successful DPipe::Write() operation will
+return amount of data written to the pipe.If a request status object has been registered
+for Data Available notification, it will complete.
+
+
+@param aBuf Buffer from which data need to be written to the pipe.
+
+@param aSize Amount of data to be written to the pipe.
+
+@return >0 Amount of data written to the pipe, in octets.
+ KErrOverflow The pipe is full no data is written.
+ KErrArgument if the amount of data to be written in invalid
+ KErrNotReady if the read end is not opened.
+ otherwise one of the other system wide error code
+*/
+ {
+
+ if(iChannelType!= RPipe::EWriteChannel)
+ return KErrAccessDenied;
+
+ TAutoWait<DMutex> outerAutoMutex(*iData->iWriteMutex);
+ TAutoWait<DMutex> innerAutoMutex(iData->Mutex());
+
+ if(!(iData->IsReadEndOpened()))
+ {
+ return KErrNotReady;
+ }
+
+ return iData->Write(aBuff, aSize);
+ }
+
+
+
+TInt DPipeChannel::CloseHandle()
+/**
+Attempts to close the pipe for reading or writing .
+
+@param None
+
+@return KErrNone Success.
+ KErrCouldNotDisconnect The pipe is already closed for that operation.
+
+*/
+ {
+ if(iData==NULL)
+ {
+ return KErrNone;
+ }
+
+ __KTRACE_OPT(KPIPE, Kern::Printf("DPipeChannel::CloseHandle ID=%d, ChannelType=%d", iData->OpenId(), iChannelType));
+
+ NKern::ThreadEnterCS();
+ iData->Wait();
+ TInt err = KErrNone;
+ if(iChannelType == RPipe::EReadChannel)
+ {
+ CancelRequest(RPipe::EDataAvailable);
+ err = iData->CloseReadEnd();
+ }
+ else if(iChannelType == RPipe::EWriteChannel)
+ {
+ CancelRequest(RPipe::ESpaceAvailable);
+ err = iData->CloseWriteEnd();
+ }
+ else
+ {
+ FAULT(); //iChannelType should be set correctly if iData was non-null
+ }
+ // If we had a pointer to the pipe but it had no back pointer
+ // to us something has gone wrong.
+ __NK_ASSERT_DEBUG(err == KErrNone);
+
+ const TInt pipeId=iData->OpenId();
+ iData->Signal();
+ iData = NULL;
+
+ // The return code from close would inform us if
+ // the device had no record of the pipe.
+ // However, for a named pipe there is no gurrantee that the pipe
+ // hasn't been deleted once we close our end of the pipe and
+ // Signal.
+ static_cast<DPipeDevice*>(iDevice)->Close(pipeId);
+
+ NKern::ThreadLeaveCS();
+
+ return err;
+ }
+
+
+
+TInt DPipeChannel::NotifySpaceAvailable ( TInt aSize,TRequestStatus* aStat, TBool aAllowDisconnected)
+/**
+Registers the request status object to be completed when space becomes
+available in the pipe.
+
+@param aSize The size for which the user has requested for notification
+
+@param aStat Status request to be registered
+@param aAllowDisconnected If false then confirm that the pipe has a reader
+
+@return KErrNone Success in registering the request
+ KErrAccessDenied If the correct end is not used to register the request
+ KErrInUse A notifier of this type has already been registered.
+ otherwise one of the other system wide error code.
+ KErrNotReady The pipe has no reader
+*/
+ {
+
+ //! Check if correct end is used
+ if(iChannelType!= RPipe::EWriteChannel)
+ {
+ return KErrAccessDenied;
+ }
+
+ TAutoWait<DMutex> autoMutex(iData->Mutex());
+ //Check if there is already a pending Space Available request.
+ if(iClientRequest->StatusPtr())
+ {
+ return KErrInUse;
+ }
+ else
+ {
+ if(!aAllowDisconnected && !(iData->IsReadEndOpened()) )
+ return KErrNotReady;
+
+ TInt r = iClientRequest->SetStatus(aStat);
+ __NK_ASSERT_ALWAYS(KErrNone == r); //we just checked StatusPtr
+ DThread* const currThread = &Kern::CurrentThread();
+
+ if((iData->RegisterSpaceAvailableNotification(aSize))==KErrCompletion)
+ {
+ Kern::QueueRequestComplete(currThread, iClientRequest, KErrNone);
+ }
+ else
+ {
+ iRequestThread = currThread;
+ // Open a reference on client thread so its control block can't disappear until
+ // this channel has finished with it.
+ iRequestThread->Open();
+ iRequestType = RPipe::ESpaceAvailable;
+ }
+ }
+ return KErrNone;
+ }
+
+
+TInt DPipeChannel::NotifyDataAvailable (TRequestStatus* aStat, TBool aAllowDisconnected)
+/**
+Registers the request status object to be completed when data becomes
+available in the pipe.
+
+@param aStat Status request to be registered
+@param aAllowDisconnected If false then fail if the pipe is empty with no writer.
+
+@return KErrNone Success in registering the request
+ KErrAccessDenied If the correct end is not used to register the request
+ KErrInUse A notifier of this type has already been registered.
+ otherwise one of the other system wide error code.
+ KErrNotReady The pipe was empty and had no writer
+*/
+ {
+
+ //! Check if correct end is used
+ if(iChannelType!= RPipe::EReadChannel)
+ {
+ return KErrAccessDenied;
+ }
+
+ // Check if there is already a pending Data Available request.
+ TAutoWait<DMutex> autoMutex(iData->Mutex() );
+ if(iClientRequest->StatusPtr())
+ {
+ return KErrInUse;
+ }
+ else
+ {
+ if(!aAllowDisconnected)
+ {
+ if(iData->IsBufferEmpty() && (!iData->IsWriteEndOpened()))
+ return KErrNotReady;
+ }
+
+ TInt r = iClientRequest->SetStatus(aStat);
+ __NK_ASSERT_ALWAYS(KErrNone == r); //we just checked StatusPtr
+ DThread* const currThread = &Kern::CurrentThread();
+
+ if((iData->RegisterDataAvailableNotification()) == KErrCompletion)
+ {
+ Kern::QueueRequestComplete(currThread, iClientRequest, KErrNone);
+ }
+ else
+ {
+ iRequestThread = currThread;
+ // Open a reference on client thread so its control block can't disappear until
+ // this channel has finished with it.
+ iRequestThread->Open();
+ iRequestType = RPipe::EDataAvailable;
+ }
+ }
+ return KErrNone;;
+ }
+
+
+TInt DPipeChannel::WaitNotification(TRequestStatus* aStat, TAny* aName, TInt aChoice)
+/**
+Registers the request status object to be completed when other end of the pipe
+is opened for reading (or writing).This method completes immediately if the other end of the
+pipe is already opened.
+
+
+@param aName Pointer to the a name passed as void pointer
+
+@param aStat Status request to be registered
+
+@param aChoice EWaitForReader,wait notification for Read end Opened.
+ EWaitForWriter,wait notification for Write end Opened.
+
+@return KErrNone Success in registering the request
+ KErrInUse A notifier of this type has already been registered.
+ KErrAccessDenied If the correct end is not used to register the request
+ otherwise one of the other system wide error code
+
+*/
+ {
+ //! Check if correct end is used
+ if(((aChoice == RPipe::EWaitForReader) && (iChannelType!= RPipe::EWriteChannel))
+ || ((aChoice == RPipe::EWaitForWriter) && (iChannelType!= RPipe::EReadChannel)))
+ {
+ return KErrAccessDenied;
+ }
+
+ TKName PName;
+ Kern::KUDesGet(PName, *(TDesC*)aName);
+
+ TAutoWait<DMutex> autoMutex(iData->Mutex());
+ if(iData->MatchName(&PName)== EFalse)
+ {
+ return KErrNotFound;
+ }
+ // Check if there is already a pending request.
+ else if(iClientRequest->StatusPtr())
+ {
+ return KErrInUse;
+ }
+ else
+ {
+ TInt r = iClientRequest->SetStatus(aStat);
+ __NK_ASSERT_ALWAYS(KErrNone == r); //we just checked StatusPtr
+ DThread* const currThread = &Kern::CurrentThread();
+
+ //register the request.
+ if((iData->RegisterWaitNotification((TInt )aChoice))== KErrCompletion)
+ {
+ Kern::QueueRequestComplete(currThread, iClientRequest, KErrNone);
+ }
+ else
+ {
+ iRequestThread = currThread;
+ // Open a reference on client thread so its control block can't disappear until
+ // this channel has finished with it.
+ iRequestThread->Open();
+ iRequestType = RPipe::EWaitNotification;
+ }
+ }
+ return KErrNone;
+ }
+
+
+/**
+For a given request return true if the notification
+we are cancelling is outstanding. If not, or
+if the supplied request is not a valid cancllation
+return false
+*/
+TBool DPipeChannel::ValidCancellation(TInt aReqType)
+{
+ switch(aReqType)
+ {
+ case RPipe::ECancelDataAvailable:
+ return (iRequestType==RPipe::EDataAvailable);
+ case RPipe::ECancelSpaceAvailable:
+ return (iRequestType==RPipe::ESpaceAvailable);
+ case RPipe::ECancelWaitNotification:
+ return (iRequestType==RPipe::EWaitNotification);
+ default:
+ return EFalse;
+ }
+}
+
+void DPipeChannel::CancelRequest ( TInt aReqType)
+/**
+Cancels an outstanding space available notifier request.
+
+@param aReqType A number identifying the message type
+
+@return None
+*/
+{
+ TAutoWait<DMutex> autoMutex(iData->Mutex() );
+ if(iClientRequest->StatusPtr() && ValidCancellation(aReqType))
+ {
+ switch(aReqType)
+ {
+ case RPipe::ECancelDataAvailable:
+ iData->CancelDataAvailable();
+ break;
+
+ case RPipe::ECancelSpaceAvailable:
+ iData->CancelSpaceAvailable();
+ break;
+
+ case RPipe::ECancelWaitNotification:
+ iData->CancelWaitNotifier();
+ break;
+
+ default:
+ FAULT();
+ }
+ Kern::QueueRequestComplete(iRequestThread, iClientRequest, KErrCancel);
+ // Close our reference on the client thread
+ Kern::SafeClose((DObject*&)iRequestThread,NULL);
+ iRequestThread = NULL;
+ }
+ return;
+ }
+
+
+TInt DPipeChannel::Size()
+/**
+Returns the size of the Pipe's buffer
+
+@param None
+
+@return TInt Return the size of the pipe, otherwise one of the other system wide
+ error code.
+*/ {
+ if(!iData)
+ return KErrNotReady;
+ else
+ return iData->Size();
+ }
+
+
+void DPipeChannel::Flush()
+/*
+Flush the content of the pipe
+
+@param None
+@pre Must be in a critical section.
+@return None
+
+*/ {
+ //The flush is, in effect, a read where the data is ignored
+ TAutoWait<DMutex> autoMutex(*iData->iReadMutex);
+
+ iData->Wait();
+ iData->FlushPipe();
+ iData->Signal();
+ }
+
+
+// Called from the DPipe
+
+void DPipeChannel::DoRequestCallback()
+/**
+It is called from the DPipe to complete the Outstanding request
+
+@param None
+
+@return None
+*/
+ {
+ __ASSERT_MUTEX(&iData->Mutex());
+ __NK_ASSERT_DEBUG(iRequestThread);
+ Kern::QueueRequestComplete(iRequestThread, iClientRequest, KErrNone);
+ Kern::SafeClose((DObject*&)iRequestThread,NULL);
+ iRequestThread=NULL;
+ }
+
+
+
+// DPipe the Kernel side pipe representing class
+
+DPipe::~DPipe()
+/**
+Destructor
+*/
+ {
+ delete iBuffer;
+ if (iPipeMutex)
+ iPipeMutex->Close(NULL);
+ if (iReadMutex)
+ iReadMutex->Close(NULL);
+ if(iWriteMutex)
+ iWriteMutex->Close(NULL);
+ }
+
+
+// Creates a Named pipe
+DPipe* DPipe::CreatePipe(const TDesC& aName, TInt aSize, TAny *aPolicy)
+/**
+Static method to Create a Named pipe.
+@param aName Reference to the Name to be set to the current named pipe.
+@param aSize Size of the Pipe.
+@param TAny Pointer to TSecurityPolicy passed as void pointer
+
+@return DPipe* Reference to DPipe* instance if successful, otherwise NULL
+*/
+ {
+
+ DPipe* tmp = new DPipe;
+ if (!tmp)
+ {
+ return NULL;
+ }
+ if(tmp->ConstructPipe(aName, aSize, aPolicy)!= KErrNone)
+ {
+ delete tmp;
+ return NULL;
+ }
+ return tmp;
+ }
+
+
+TInt DPipe::ConstructPipe(const TDesC& aName, TInt aSize,TAny* aPolicy)
+/**
+Second phase constructor
+
+@param aName The name of the pipe to be created
+@param aSize The size of the pipe to be created
+@param TAny Pointer to TSecurityPolicy passed as void pointer
+
+@return KErrNone If successful, otherwise one of the other system wide error code
+*/
+ {
+ // check the size parameter.
+ if(aPolicy)
+ {
+
+ memcpy(&iPolicy,aPolicy,sizeof(TSecurityPolicy));
+
+ }
+ else
+ {
+ TSecurityPolicy apolicy(ECapability_None);
+ memcpy(&iPolicy,&apolicy,sizeof(TSecurityPolicy));
+ }
+
+ if(aName.Length() != 0)
+ {
+ iName.Copy(aName);
+ }
+
+ iBuffer = static_cast<TUint8*>(Kern::AllocZ(aSize));
+ if(!iBuffer)
+ return KErrNoMemory;
+
+ // Initialisation
+ _LIT(KMutexName,"PipeMutex");
+ TInt err = Kern::MutexCreate(iPipeMutex, KMutexName, KMutexOrdGeneral0);
+ if (err)
+ {
+ return err;
+ }
+ _LIT(KReadMutex,"ReadMutex");
+ err = Kern::MutexCreate(iReadMutex, KReadMutex, KMutexOrdGeneral1);
+ if (err)
+ {
+ return err;
+ }
+
+ _LIT(KWriteMutex,"WriteMutex");
+ err = Kern::MutexCreate(iWriteMutex, KWriteMutex, KMutexOrdGeneral1);
+ if (err)
+ {
+ return err;
+ }
+
+ iSize = aSize;
+ iWritePointer = iReadPointer = 0;
+ iFull = EFalse;
+ return KErrNone;
+ }
+
+
+TInt DPipe::OpenId()
+/**
+Returns the id of the Pipe
+
+@param None
+
+@return iID ID of the pipe
+*/
+ {
+ //could be const
+ return iID;
+ }
+
+
+void DPipe::SetId(TInt aId)
+/**
+Set the id of the Pipe
+
+@param aId The id to be set
+
+@return None
+*/
+ {
+ //this is only called by the pipe device
+ //it could also be set at construction time
+ iID = aId;
+ }
+
+
+TBool DPipe::IsPipeClosed()
+/**
+Check if the Pipe is Closed.
+@param None
+@return TBool ETure if Successful, otherwise EFalse;
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+
+ return !(iReadChannel || iWriteChannel);
+ }
+
+
+TBool DPipe::MatchName(const TDesC8* aName)
+/**
+Check if the current instance of DPipe Name is matching with aName parameter
+
+@param aName Name to be checked with the current DPipe's name.
+
+@return TBool ETrue if match found, otherwise EFalse
+*/
+ {
+ //name could be const
+ return (iName.Compare(*aName) == 0);
+ }
+
+
+TBool DPipe::MatchId(const TInt aId)
+/**
+Checks if the current instance of DPipe is matching with the aId parameter
+
+@param aId ID to be checked with the current DPipe's id
+
+@return TBool ETure if match found , otherwise EFalse;
+*/
+ {
+ return (iID == aId);
+ }
+
+
+TBool DPipe::IsBufferEmpty()
+/**
+Checks if the Buffer is Empty
+
+@param None
+@return ETrue if buffer is empty
+*/
+ {
+ return (AvailableDataCount()==0);
+ }
+
+
+TInt DPipe::Write(TAny* aBuf, TInt aSize)
+/**
+Synchronous, non-blocking write operation. If the pipe is full it will
+return immediately with KErrOverflow. A successful DPipe::Write() operation will
+return amount of data written to the pipe.If a request status object has been registered
+for Data Available notification, it will complete.
+
+@param aBuf Buffer from which data need to be written to the pipe.
+@param aSize Amount of data to be written to the pipe.
+
+@return >0 Amount of data written to the pipe, in octets.
+ KErrNone No data written to the pipe.
+ KErrOverflow Pipe is full, cannot write any more data.
+ KErrArgument If the amount of data to be written is invalid.
+ Otherwise one of the other system wide error code
+
+@pre iPipeMutex held
+@pre iWriteMutex held
+
+@note Write enters and exists with the pipe mutex held - but releases and reaquires internally
+*/
+ {
+ __KTRACE_OPT(KPIPE, Kern::Printf("DPipe::Write(aBuf=0x%08x, aSize=%d)", aBuf, aSize));
+
+ __ASSERT_MUTEX(iPipeMutex);
+ __ASSERT_MUTEX(iWriteMutex);
+ // Check for the Invalid Length
+ if(aSize < 0)
+ {
+ return KErrArgument;
+ }
+
+ if(aSize == 0)
+ {
+ return KErrNone;
+ }
+
+ //Since only one thread can be writing to the write end
+ //of a pipe it is sufficient that AvailableDataCount
+ //holds the pipe mutex. After it returns the
+ //available space may increase
+ //but can not decrease
+ const TInt spaceavailable = (iSize - AvailableDataCount());
+ if (spaceavailable < aSize)
+ {
+ //Though the API may suggest otherwise - partial writes are not supported.
+ return KErrOverflow;
+ }
+
+ //release mutex before IPC read
+ Signal();
+
+ //First half
+ const TDesC8* pBuf = (const TDesC8*)aBuf;
+
+ const TInt distanceToEnd = iSize - iWritePointer;
+ const TInt firstHalf = Min(distanceToEnd, aSize);
+ TPtr ptr(&iBuffer[iWritePointer], firstHalf);
+
+ DThread* const currThread = &Kern::CurrentThread();
+ TInt r=Kern::ThreadDesRead(currThread, pBuf, ptr, 0, KChunkShiftBy0);
+ if(r!=KErrNone)
+ {
+ Wait(); //we must exit with mutex held
+ return r;
+ }
+
+ //Second half
+ const TInt secondHalf = aSize - firstHalf;
+ __NK_ASSERT_DEBUG( secondHalf >= 0);
+ if(secondHalf != 0)
+ {
+ ptr.Set(&iBuffer[0], secondHalf, secondHalf);
+
+ r = Kern::ThreadDesRead(currThread, pBuf, ptr, firstHalf, KChunkShiftBy0);
+ if(r!=KErrNone)
+ {
+ Wait(); //we must exit with mutex held
+ return r;
+ }
+ }
+
+ Wait(); //reaquire mutex for state update
+ iWritePointer = (iWritePointer + aSize)% iSize;
+
+ if(iWritePointer == iReadPointer)
+ {
+ iFull = ETrue;
+ }
+
+ if(iDataAvailableRequest)
+ {
+ iReadChannel->DoRequestCallback();
+ iDataAvailableRequest = EFalse;
+ }
+
+ return aSize;
+ }
+
+
+TInt DPipe::Read(TAny* aBuf, TInt aSize)
+/**
+Synchronous, non-blocking read operation. If the pipe is empty it will
+return immediately with KErrUnderflow. A successful DPipe::Read() operation
+will free up more space in the pipe. If a request status object has been registered
+for Space Available notification, it will complete. Note that there is no
+guarantee that the amount of space freed up in the pipe will be sufficient
+for the next DPipe::Write() operation.
+
+@param aBuff Buffer to which data need to be written.
+@param aSize Size of the data to be read from the pipe.
+
+@return >0 Amount of data read from the pipe, in octets.
+ KErrNone The pipe is empty , no data was read from the pipe.
+ KErrArgument If the amount of data to be read is invalid.
+ Otherwise one of the system wide error code
+@pre iPipeMutex held
+@pre iReadMutex held
+
+@note Read enters and exists with the pipe mutex held - but releases and reaquires internally
+*/
+ {
+ __KTRACE_OPT(KPIPE, Kern::Printf("DPipe::Read(aBuf=0x%08x, aSize=%d)", aBuf, aSize));
+ __ASSERT_MUTEX(iPipeMutex);
+ __ASSERT_MUTEX(iReadMutex);
+
+ if(aSize < 0)
+ {
+ return KErrArgument;
+ }
+
+ const TInt totalToRead = Min(AvailableDataCount(), aSize);
+
+
+ if(totalToRead == 0)
+ return 0;
+
+ Signal();
+
+
+ //! First half
+ const TInt distanceToEnd = iSize - iReadPointer;
+ __NK_ASSERT_DEBUG(distanceToEnd>=0);
+ const TInt firstHalf = Min(totalToRead, distanceToEnd);
+
+ TPtrC8 pipeBuffer(&iBuffer[iReadPointer], firstHalf);
+ TDes8* userBuffer = (TDes8*)aBuf;
+
+ DThread* const currThread = &Kern::CurrentThread();
+ TInt r = Kern::ThreadDesWrite(currThread, userBuffer, pipeBuffer, 0, KChunkShiftBy0, NULL);
+ if(r!=KErrNone)
+ {
+ Wait(); //we must exit with mutex held
+ return r;
+ }
+
+ const TInt secondHalf=totalToRead-firstHalf;
+ __NK_ASSERT_DEBUG(secondHalf>=0);
+ if(secondHalf!=0)
+ {
+ //! Second half
+ pipeBuffer.Set(&iBuffer[0], secondHalf);
+ r = Kern::ThreadDesWrite(currThread, userBuffer, pipeBuffer, firstHalf, KChunkShiftBy0, NULL);
+ if(r!=KErrNone)
+ {
+ Wait(); //we must exit with mutex held
+ return r;
+ }
+ }
+ __NK_ASSERT_DEBUG(firstHalf+secondHalf==totalToRead);
+
+ Wait(); //Reaquire mutex for state update
+
+ iReadPointer = (iReadPointer + totalToRead)% iSize;
+ iFull = EFalse;
+ MaybeCompleteSpaceNotification();
+
+ __ASSERT_MUTEX(iReadMutex);
+ return totalToRead;
+ }
+
+TInt DPipe::AvailableDataCount()
+/**
+Returns the Data available in the pipe.
+
+@param None
+
+@return TInt Amount of data available in the pipe
+
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+ TInt size=-1;
+ if ( iWritePointer > iReadPointer )
+ {
+ size = iWritePointer - iReadPointer;
+ }
+ else if ( iReadPointer > iWritePointer )
+ {
+ size = iSize - iReadPointer + iWritePointer;
+ }
+ else
+ {
+ //iReadPointer == iWritePointer
+ size = iFull ? iSize : 0;
+ }
+ return size;
+ }
+
+TInt DPipe::RegisterSpaceAvailableNotification(TInt aSize)
+/**
+Registers the request status object to be completed when space becomes
+available in the pipe.
+
+@param aSize The size for which the space availability be notified.
+
+@return KErrNone Success.
+ KErrCompletion The request is not registered as it completes immediately
+ otherwise one of the system wide error code.
+@pre Mutex must be held.
+@pre Must be in a critical section.
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+ __NK_ASSERT_DEBUG(Rng(1, aSize, iSize));
+
+ // Check if Specified size is available.
+ TInt err = KErrNone;
+ if ((aSize <= (iSize - AvailableDataCount())))
+ {
+ iSpaceAvailableRequest = EFalse;
+ err = KErrCompletion;
+ }
+ else
+ {
+ iSpaceAvailableSize = aSize;
+ iSpaceAvailableRequest = ETrue;
+ }
+ return err;
+ }
+
+
+TInt DPipe::RegisterDataAvailableNotification()
+/**
+Registers the request status object to be completed when data becomes
+available in the pipe.
+
+@param None
+
+@return KErrNone If successful, otherwise one of the other system wide
+ error code.
+@pre Mutex must be held.
+@pre Must be in a critical section.
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+
+ TInt err = KErrNone;
+ // Check if Data is available.
+ if(AvailableDataCount())
+ {
+ iDataAvailableRequest = EFalse;
+ err = KErrCompletion;
+ }
+ else
+ {
+ iDataAvailableRequest = ETrue;
+ }
+ return err;
+ }
+
+
+TInt DPipe::RegisterWaitNotification(TInt aChoice)
+/**
+Registers the request status object to be completed when other end of the pipe
+is opened for reading. This method completes immediately if the other end of the
+pipe is already opened.
+
+@param None
+
+@return KErrNone Successfully registered, otherwise one of the other system wide
+ error code.
+@pre Mutex must be held.
+@pre Must be in a critical section.
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+
+ TInt err = KErrNone;
+ // Check if Read end is opened
+ if (aChoice == RPipe::EWaitForReader)
+ {
+ if(IsReadEndOpened())
+ {
+ iWaitRequest = EFalse;
+ err = KErrCompletion;
+ }
+ else
+ {
+ iWaitRequest = ETrue;
+ }
+
+ }
+ else
+ {
+ if(IsWriteEndOpened())
+ {
+ iWaitRequest = EFalse;
+ err = KErrCompletion;
+ }
+ else
+ {
+ iWaitRequest = ETrue;
+ }
+ }
+
+ return err;
+ }
+
+
+//! Cancellation methods
+void DPipe::CancelSpaceAvailable()
+/**
+Cancels an outstanding space available notifier request.
+
+@param None
+
+@return None
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+ if(iSpaceAvailableRequest)
+ iSpaceAvailableRequest = EFalse;
+ }
+
+
+void DPipe::CancelDataAvailable()
+/**
+Cancels an outstanding data available notifier request.
+
+@param None
+
+@return None
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+ if(iDataAvailableRequest)
+ iDataAvailableRequest = EFalse;
+ }
+
+
+void DPipe::CancelWaitNotifier()
+/**
+Cancel an outstanding wait notifier request
+
+@param None
+
+@return KErrNone If Successful, otherwise one of the other system wide error code.
+
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+ // Cancel Wait Notifier request
+ if(iWaitRequest)
+ iWaitRequest = EFalse;
+ }
+
+
+void DPipe::CloseAll()
+/**
+Cancel any outstanding request.
+
+@param None
+
+@return None
+*/
+ {
+ CancelSpaceAvailable();
+ CancelDataAvailable();
+ CancelWaitNotifier();
+
+ CloseWriteEnd();
+ CloseReadEnd();
+ }
+
+
+TInt DPipe::CloseReadEnd()
+/**
+Close the read end of the pipe.
+
+Cancels outstanding requests placed by the *write*
+channel and clears pipe's pointer to the read channel.
+If this function is called then the read channel's back
+pointer to the pipe must also be cleared.
+
+@param None
+
+@return KErrNone If the end is closed, else one of the other system
+ wide error code
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+ __KTRACE_OPT(KPIPE, Kern::Printf(">DPipe::CloseReadEnd ID=%d, iReadChannel=0x%08x", OpenId(), iReadChannel));
+
+ if (!iReadChannel)
+ return KErrCouldNotDisconnect;
+ else
+ {
+ if(iWriteChannel)
+ {
+ iWriteChannel->CancelRequest(RPipe::ECancelSpaceAvailable);
+ }
+ iReadChannel = NULL;
+ }
+ return KErrNone;
+ }
+
+
+TInt DPipe::CloseWriteEnd()
+/**
+Close the write end of the pipe
+
+Cancels outstanding requests placed by the *read*
+channel and clears pipe's pointer to the write channel.
+If this function is called then the write channel's back
+pointer to the pipe must also be cleared.
+
+@param None
+
+@return KErrNone If the write end is successfully closed, else
+ one of the other system wide error code.
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+ __KTRACE_OPT(KPIPE, Kern::Printf(">DPipe::CloseWriteEnd ID=%d, iWriteChannel=0x%08x", OpenId(), iWriteChannel));
+
+ if (!iWriteChannel)
+ return KErrCouldNotDisconnect;
+ else
+ {
+ // Cancel RBlocking call if it is there
+ if(iReadChannel)
+ {
+ iReadChannel->CancelRequest(RPipe::ECancelDataAvailable);
+ }
+ iWriteChannel = NULL;
+ }
+ return KErrNone;
+ }
+
+
+
+void DPipe::FlushPipe()
+/**
+Flush all the date from the pipe and reinitialise the buffer pointer.
+
+@param None
+
+@return None
+
+@pre Pipe Mutex to be held
+@pre Read Mutex to be held
+
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+ __ASSERT_MUTEX(iReadMutex);
+
+ iReadPointer = iWritePointer;
+ iFull = EFalse;
+
+ MaybeCompleteSpaceNotification();
+ }
+
+/**
+If there is an outstanding space request, and
+there is enough space to satisfy it then complete
+and clear request.
+
+@pre the pipe mutex must be held
+*/
+void DPipe::MaybeCompleteSpaceNotification()
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+
+ // Check if there is writeblocking request
+ if(iSpaceAvailableRequest)
+ {
+ const TInt spacecount = (iSize - AvailableDataCount());
+ if (iSpaceAvailableSize <= spacecount)
+ {
+ iWriteChannel->DoRequestCallback();
+ iSpaceAvailableRequest = EFalse;
+ }
+ }
+ }
+
+TBool DPipe::IsReadEndOpened()
+/**
+Returns information regarding the read end of the current pipe instance.
+
+@return TBool ETrue if read end is Opened, otherwise EFalse
+@pre the pipe mutex must be held
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+ return (iReadChannel != NULL);
+ }
+
+
+TBool DPipe::IsWriteEndOpened()
+/**
+Returns information regarding the write end of the current pipe instance.
+
+@return TBool ETrue if WriteChannel is opened, otherwise EFalse
+@pre the pipe mutex must be held
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+ return (iWriteChannel != NULL);
+ }
+
+
+TBool DPipe::IsNamedPipe()
+/**
+Returns whether the pipe is named or unnamed.
+
+@return TBool ETrue if it is a named pipe, otherwise EFalse
+
+*/
+ {
+ return (iName.Length() != 0);
+ }
+
+
+void DPipe::SetReadEnd(DPipeChannel* aChannel)
+/**
+Set the Read end of the pipe as opened and store the pointer for the read channel
+It also notify if there is any pending Wait Request.
+
+@param aChannel The pointer to the read channel
+
+@pre the pipe mutex must be held
+@pre The pipe's read end must be closed ie. IsReadEndOpened returns false
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+ __KTRACE_OPT(KPIPE, Kern::Printf(">DPipe::SetReadEnd ID=%d", OpenId()));
+
+
+ //A channel must be sure this function
+ //succeeded otherwise the pipe
+ //could be destroyed without the channel's
+ //knowledge
+ __NK_ASSERT_DEBUG(iReadChannel==NULL);
+
+ iReadChannel = aChannel;
+ __KTRACE_OPT(KPIPE, Kern::Printf("DPipe::SetReadEnd set iReadChannel=0x%08x", iReadChannel));
+
+ if(iWaitRequest)
+ {
+ if(iWriteChannel)
+ iWriteChannel->DoRequestCallback();
+ iWaitRequest=EFalse;
+ }
+ }
+
+
+void DPipe::SetWriteEnd(DPipeChannel* aChannel)
+/**
+Set the write end of the pipe as opened and store the pointer to the write channel
+
+@param aChannel The pointer to the write channel
+
+
+@pre the pipe mutex must be held
+@pre The pipe's write end must be closed ie. IsWriteEndOpened returns false
+*/
+ {
+ __ASSERT_MUTEX(iPipeMutex);
+ __KTRACE_OPT(KPIPE, Kern::Printf(">DPipe::SetWriteEnd ID=%d", OpenId()));
+
+ //A channel must be sure this function
+ //succeeded otherwise the pipe
+ //could be destroyed without the channel's
+ //knowledge
+ __NK_ASSERT_DEBUG(iWriteChannel==NULL);
+
+ iWriteChannel = aChannel;
+ __KTRACE_OPT(KPIPE, Kern::Printf("DPipe::SetWriteEnd set iWriteChannel=0x%08x", iWriteChannel));
+
+ if(iWaitRequest)
+ {
+ if(iReadChannel)
+ iReadChannel->DoRequestCallback();
+ iWaitRequest=EFalse;
+ }
+ }
+
+TInt DPipe::Size()
+/**
+@return The size of the pipe's circular buffer
+*/
+ {
+ //this could be const
+ return iSize;
+ }
+