sl@0: // Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0: // All rights reserved.
sl@0: // This component and the accompanying materials are made available
sl@0: // under the terms of "Eclipse Public License v1.0"
sl@0: // which accompanies this distribution, and is available
sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0: //
sl@0: // Initial Contributors:
sl@0: // Nokia Corporation - initial contribution.
sl@0: //
sl@0: // Contributors:
sl@0: //
sl@0: // Description:
sl@0: // Implementation of pipes
sl@0: // 
sl@0: //
sl@0: 
sl@0: #include "POSIXIF.H"
sl@0: #include <sys/errno.h>
sl@0: #include <sys/stat.h>
sl@0: 
sl@0: // Child end of the pipe
sl@0: 
sl@0: CPipeChildDesc::CPipeChildDesc(TInt anIndex, RPosixSession& aSession)
sl@0: 		: CFileDescBase(), iIndex(anIndex), iSession(aSession), iParamDes(0,0,0)
sl@0: 	{}
sl@0: 
sl@0: TInt CPipeChildDesc::LSeek (int&, int)
sl@0: 	{
sl@0: 	return ESPIPE;	// can't seek on a pipe
sl@0: 	}
sl@0: 
sl@0: TInt CPipeChildDesc::FStat(struct stat *st)
sl@0: 	{
sl@0: 	// I am a fifo about which little is known
sl@0: 	st->st_mode = S_IFIFO;
sl@0: 	st->st_blksize=0;
sl@0: 	return KErrNone;
sl@0: 	}
sl@0: 
sl@0: void CPipeChildDesc::Read (TDes8& aBuf, TRequestStatus& aStatus)
sl@0: 	{
sl@0: 	if (iClientClosed || !IsReadable())
sl@0: 		{
sl@0: 		Complete(aStatus,KErrEof);
sl@0: 		return;
sl@0: 		}
sl@0: 	TIpcArgs args(iIndex,&aBuf,aBuf.MaxLength());
sl@0: 	iSession.Request(PMPipeRead,args,aStatus);	// asynchronous request
sl@0: 	}
sl@0: 
sl@0: void CPipeChildDesc::ReadCancel()
sl@0: 	{
sl@0: 	Cancel(PMPipeRead);
sl@0: 	}
sl@0: 
sl@0: TInt CPipeChildDesc::ReadCompletion(TDes8& aDesc, TInt aStatus)
sl@0: 	{
sl@0: 	if (aStatus==KErrEof)
sl@0: 		{
sl@0: 		ClientClose();
sl@0: 		aDesc.Zero();		// set read length to zero
sl@0: 		return KErrNone;	// indicates graceful close at the other end
sl@0: 		}
sl@0: 	return aStatus;
sl@0: 	}
sl@0: 
sl@0: void CPipeChildDesc::Write(TDes8& aDesc, TRequestStatus& aStatus)
sl@0: 	{
sl@0: 	if (iClientClosed || !IsWriteable())
sl@0: 		{
sl@0: 		Complete(aStatus,KErrEof);
sl@0: 		return;
sl@0: 		}
sl@0: 	TIpcArgs args(iIndex,&aDesc,aDesc.Length());
sl@0: 	iSession.Request(PMPipeWrite,args,aStatus);	// asynchronous request
sl@0: 	}
sl@0: 
sl@0: void CPipeChildDesc::WriteCancel()
sl@0: 	{
sl@0: 	Cancel(PMPipeWrite);
sl@0: 	}
sl@0: 
sl@0: TInt CPipeChildDesc::WriteCompletion(TDes8& /*aDesc*/, TInt aStatus)
sl@0: 	{
sl@0: 	if (aStatus==KErrEof)
sl@0: 		ClientClose();
sl@0: 	return aStatus;
sl@0: 	}
sl@0: 
sl@0: void CPipeChildDesc::Ioctl(int aCmd, void* aParam, TRequestStatus& aStatus)
sl@0: //
sl@0: // The work of the Ioctl is done in the parent, including writing back to aParam?
sl@0: // Use the default completion which just returns aStatus.Int()
sl@0: //
sl@0: 	{
sl@0: 	if (iClientClosed)
sl@0: 		{
sl@0: 		Complete(aStatus,KErrEof);
sl@0: 		return;
sl@0: 		}
sl@0: 	iParamDes.Set((TText8*)aParam,4,4);
sl@0: 	TIpcArgs args(iIndex,aCmd,&iParamDes);	
sl@0: 	if (aCmd==E32IOSELECT)
sl@0: 		args.Set(3, *((TInt*)aParam));
sl@0: 	iSession.Request(PMPipeIoctl,args,aStatus);	// asynchronous request
sl@0: 	}
sl@0: 
sl@0: void CPipeChildDesc::IoctlCancel()
sl@0: 	{
sl@0: 	Cancel(PMPipeIoctl);
sl@0: 	}
sl@0: 
sl@0: void CPipeChildDesc::Cancel(TInt aType)
sl@0: 	{
sl@0: 	if (iClientClosed)
sl@0: 		return;
sl@0: 	TIpcArgs args(iIndex,aType);
sl@0: 	iSession.Request(PMPipeCancel,args);
sl@0: 	}	
sl@0: 
sl@0: TInt CPipeChildDesc::FinalClose()
sl@0: 	{
sl@0: 	ClientClose();
sl@0: 	TIpcArgs args(iIndex);
sl@0: 	return iSession.Request(PMPipeClose,args);		// synchronous request
sl@0: 	}
sl@0: 
sl@0: // 
sl@0: // Parent end of the pipe, where the real work is done
sl@0: //
sl@0: 
sl@0: CPipeDesc::CPipeDesc(TInt anIndex) : CFileDescBase()
sl@0: 	{
sl@0: 	iIndex=anIndex;
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::SetClientSide(CPipeDesc*& aClientPointer)
sl@0: 	{
sl@0: 	iClientSide=&aClientPointer;
sl@0: 	}
sl@0: 
sl@0: _LIT(KCPipeDescPanic, "CPipeDesc");
sl@0: void CPipeDesc::Panic(TInt aReason)
sl@0: 	{
sl@0: 	User::Panic(KCPipeDescPanic,aReason);
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::Panic(RMessage2& aMessage, TInt aReason)
sl@0: 	{
sl@0: 	aMessage.Panic(KCPipeDescPanic,aReason);
sl@0: 	}
sl@0: 
sl@0: TInt CPipeDesc::LSeek (int&, int)
sl@0: 	{
sl@0: 	return ESPIPE;	// can't seek on a pipe
sl@0: 	}
sl@0: 
sl@0: TInt CPipeDesc::FStat(struct stat *st)
sl@0: 	{
sl@0: 	// I am a fifo about which little is known
sl@0: 	st->st_mode = S_IFIFO;
sl@0: 	st->st_blksize=0;
sl@0: 	return KErrNone;
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::Read (TDes8& aBuf, TRequestStatus& aStatus)
sl@0: 	{
sl@0: 	if (!IsReadable())
sl@0: 		{
sl@0: 		Complete(aStatus, KErrEof);	// avoids treading on iStatus of pending Write
sl@0: 		return;
sl@0: 		}
sl@0: 	__ASSERT_DEBUG(iStatus==0,Panic(1));
sl@0: 	iStatus=&aStatus;
sl@0: 	if (iClientClosed)
sl@0: 		{
sl@0: 		User::RequestComplete(iStatus,KErrEof);
sl@0: 		return;
sl@0: 		}
sl@0: 	iReadBuf=&aBuf;
sl@0: 	if (ClientIoctlPending())
sl@0: 		CompleteClientIoctl();
sl@0: 	if (iClientLength!=0)
sl@0: 		TransferFromClient();
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::ReadCancel()
sl@0: 	{
sl@0: 	Cancel();
sl@0: 	}
sl@0: 
sl@0: TInt CPipeDesc::ReadCompletion(TDes8& aDesc, TInt aStatus)
sl@0: 	{
sl@0: 	if (aStatus==KErrEof)
sl@0: 		{
sl@0: 		ClientClose();
sl@0: 		aDesc.Zero();		// set read length to zero
sl@0: 		return KErrNone;	// indicates graceful close at the other end
sl@0: 		}
sl@0: 	return aStatus;
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::Write(TDes8& aDesc, TRequestStatus& aStatus)
sl@0: 	{
sl@0: 	if (!IsWriteable())
sl@0: 		{
sl@0: 		Complete(aStatus, KErrEof);	// avoids treading on iStatus of pending Read
sl@0: 		return;
sl@0: 		}
sl@0: 	__ASSERT_DEBUG(iStatus==0,Panic(2));
sl@0: 	iStatus=&aStatus;
sl@0: 	if (iClientClosed)
sl@0: 		{
sl@0: 		User::RequestComplete(iStatus,KErrEof);
sl@0: 		return;
sl@0: 		}
sl@0: 	iWriteBuf.Set(aDesc);
sl@0: 	if (ClientIoctlPending())
sl@0: 		CompleteClientIoctl();
sl@0: 	if (iClientLength!=0)
sl@0: 		TransferToClient();
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::WriteCancel()
sl@0: 	{
sl@0: 	Cancel();
sl@0: 	}
sl@0: 
sl@0: TInt CPipeDesc::WriteCompletion(TDes8& /*aDesc*/, TInt aStatus)
sl@0: 	{
sl@0: 	if (aStatus==KErrEof)
sl@0: 		ClientClose();
sl@0: 	return aStatus;
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::Ioctl(int aCmd, void* aParam, TRequestStatus& aStatus)
sl@0: 	{
sl@0: 	TInt ret=KErrNone;
sl@0: 	iIoctlStatus=&aStatus;
sl@0: 	int *param=REINTERPRET_CAST(int*,aParam);
sl@0: 	switch (aCmd)
sl@0: 		{
sl@0: 	case E32IONREAD:
sl@0: 		// synchronous ioctls are handled in the completion routine.
sl@0: 		break;
sl@0: 	case E32IOSELECT:
sl@0: 		{
sl@0: 		int mask=(*param)&SelectMask();
sl@0: 		if (mask!=0 && iClientLength==0)
sl@0: 			return;	// wait for client to show up
sl@0: 		}
sl@0: 		break;
sl@0: 	default:
sl@0: 		ret=KErrNotSupported;
sl@0: 		break;
sl@0: 		}
sl@0: 	User::RequestComplete(iIoctlStatus,ret);
sl@0: 	}
sl@0: 
sl@0: TInt CPipeDesc::IoctlCompletion(int aCmd, void* aParam, TInt aStatus)
sl@0: 	{
sl@0: 	TInt ret=aStatus;
sl@0: 	if (ret!=KErrNone)
sl@0: 		return ret;
sl@0: 	int *param=REINTERPRET_CAST(int*,aParam);
sl@0: 	switch (aCmd)
sl@0: 		{
sl@0: 	case E32IONREAD:
sl@0: 		if (IsReadable())
sl@0: 			*param=iClientLength;	// 0 if no outstanding client data
sl@0: 		else
sl@0: 			*param=0;	// claim that no data is available
sl@0: 		break;
sl@0: 	case E32IOSELECT:
sl@0: 		{
sl@0: 		int mask=0;
sl@0: 		if (iClientLength!=0)
sl@0: 			mask = SelectMask();
sl@0: 		*param=(*param)&mask;
sl@0: 		}
sl@0: 		break;
sl@0: 	default:
sl@0: 		ret=KErrNotSupported;
sl@0: 		break;
sl@0: 		}
sl@0: 	return ret;
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::IoctlCancel()
sl@0: 	{
sl@0: 	User::RequestComplete(iIoctlStatus,KErrCancel);
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::Cancel()
sl@0: 	{
sl@0: 	// Pipes are unidirectional, so don't need to distinguish between
sl@0: 	// ReadCancel and WriteCancel
sl@0: 	User::RequestComplete(iStatus,KErrCancel);
sl@0: 	}
sl@0: 
sl@0: // Client-side interface
sl@0: 
sl@0: void CPipeDesc::ClientWrite(const RMessage2& aMessage)
sl@0: 	{
sl@0: 	__ASSERT_DEBUG(iClientLength==0,Panic(3));
sl@0: 	if (iClientClosed)
sl@0: 		{
sl@0: 		aMessage.Complete(KErrEof);
sl@0: 		return;
sl@0: 		}
sl@0: 	iClientLength=aMessage.Int2();
sl@0: 	iClientOffset=0;
sl@0: 	iMessage=aMessage;
sl@0: 	if (iIoctlStatus!=0)
sl@0: 		User::RequestComplete(iIoctlStatus,KErrNone);
sl@0: 	if (iStatus!=0)
sl@0: 		TransferFromClient();
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::ClientRead(const RMessage2& aMessage)
sl@0: 	{
sl@0: 	__ASSERT_DEBUG(iClientLength==0,Panic(4));
sl@0: 	if (iClientClosed)
sl@0: 		{
sl@0: 		aMessage.Complete(KErrEof);
sl@0: 		return;
sl@0: 		}
sl@0: 	iClientLength=aMessage.Int2();
sl@0: 	iMessage=aMessage;
sl@0: 	if (iIoctlStatus!=0)
sl@0: 		User::RequestComplete(iIoctlStatus,KErrNone);
sl@0: 	if (iStatus!=0)
sl@0: 		TransferToClient();
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::ClientIoctl(const RMessage2& aMessage)
sl@0: 	{
sl@0: 	__ASSERT_DEBUG(!ClientIoctlPending(),Panic(7));
sl@0: 	if (iClientClosed)
sl@0: 		{
sl@0: 		aMessage.Complete(KErrEof);
sl@0: 		return;
sl@0: 		}
sl@0: 	iClientIoctlPending=1;
sl@0: 	iIoctlMessage=aMessage;
sl@0: 	TInt ret=KErrNone;
sl@0: 	switch (aMessage.Int1())
sl@0: 		{
sl@0: 	case E32IONREAD:
sl@0: 		// synchronous ioctls are handled in the completion routine.
sl@0: 		break;
sl@0: 	case E32IOSELECT:
sl@0: 		{
sl@0: 		int mask=aMessage.Int3();
sl@0: 		mask&=ClientSelectMask();
sl@0: 		if (mask!=0 && iStatus==0)
sl@0: 			return;	// wait for parent activity
sl@0: 		}
sl@0: 		break;
sl@0: 	default:
sl@0: 		ret=KErrNotSupported;
sl@0: 		break;
sl@0: 		}
sl@0: 	CompleteClientIoctl(ret);
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::ClientCancel(const RMessage2& aMessage)
sl@0: 	{
sl@0: 	if (aMessage.Int1()==PMPipeIoctl)
sl@0: 		{
sl@0: 		if (ClientIoctlPending())
sl@0: 			CompleteClientIoctl(KErrCancel);
sl@0: 		return;
sl@0: 		}
sl@0: 	// Pipes are unidirectional, so Read and Write are cancelled by
sl@0: 	// cancelling the current client operation.
sl@0: 	//
sl@0: 	if (iClientLength!=0)
sl@0: 		{
sl@0: 		iMessage.Complete(KErrCancel);
sl@0: 		iClientLength=0;
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::ClientClose()
sl@0: 	{
sl@0: 	iClientClosed=1;
sl@0: 	// terminate any pending requests
sl@0: 	if (iStatus!=0)
sl@0: 		User::RequestComplete(iStatus,KErrEof);
sl@0: 	if (ClientIoctlPending())
sl@0: 		CompleteClientIoctl(KErrEof);
sl@0: 	if (iClientLength!=0)
sl@0: 		{
sl@0: 		iMessage.Complete(KErrEof);
sl@0: 		iClientLength=0;
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: TInt CPipeDesc::FinalClose()
sl@0: 	{
sl@0: 	ClientClose();
sl@0: 	if (iClientSide)
sl@0: 		{
sl@0: 		*iClientSide=0;
sl@0: 		iClientSide=0;
sl@0: 		}
sl@0: 	return KErrNone;
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::TransferFromClient()
sl@0: //
sl@0: // Handle transfer of data from client to parent.
sl@0: // Always complete the parent read, but only complete the child write when
sl@0: // all of the data has been consumed.
sl@0: //
sl@0: 	{
sl@0: 	TRAPD(err,iMessage.ReadL(1,*iReadBuf,iClientOffset));
sl@0: 	if (err)
sl@0: 		{
sl@0: 		Panic(iMessage,5);
sl@0: 		iClientLength=0;
sl@0: 		ClientClose();	// will complete the parent read
sl@0: 		return;
sl@0: 		}
sl@0: 	TInt length=iReadBuf->Length();	// record the amount of data transferred
sl@0: 	User::RequestComplete(iStatus,KErrNone);
sl@0: 	iClientOffset+=length;
sl@0: 	iClientLength-=length;
sl@0: 	if (iClientLength==0)
sl@0: 		iMessage.Complete(KErrNone);
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::TransferToClient()
sl@0: //
sl@0: // Handle transfer from parent to client
sl@0: // Always complete the client read, but only complete the parent write when
sl@0: // all of the data has been consumed.
sl@0: //
sl@0: 	{
sl@0: 	TInt err=KErrNone;
sl@0: 	TInt length=iWriteBuf.Length();
sl@0: 	TInt written=length;
sl@0: 	if (iClientLength >= length)
sl@0: 		{
sl@0: 		TRAP(err,iMessage.WriteL(1,iWriteBuf,0));
sl@0: 		}
sl@0: 	else
sl@0: 		{
sl@0: 		written=iClientLength;
sl@0: 		TRAP(err,iMessage.WriteL(1,iWriteBuf.Left(written),0));
sl@0: 		}
sl@0: 	iClientLength=0;
sl@0: 	if (err)
sl@0: 		{
sl@0: 		Panic(iMessage,6);
sl@0: 		ClientClose();	// will complete the parent write
sl@0: 		return;
sl@0: 		}
sl@0: 	iMessage.Complete(KErrNone);
sl@0: 	length-=written;
sl@0: 	if (length==0)
sl@0: 		User::RequestComplete(iStatus,KErrNone);
sl@0: 	else
sl@0: 		iWriteBuf.Set(iWriteBuf.Right(length));
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::CompleteClientIoctl(TInt ret)
sl@0: 	{
sl@0: 	if (ret!=KErrNone)
sl@0: 		{
sl@0: 		iIoctlMessage.Complete(ret);
sl@0: 		iClientIoctlPending=0;
sl@0: 		return;
sl@0: 		}
sl@0: 	CompleteClientIoctl();
sl@0: 	}
sl@0: 
sl@0: void CPipeDesc::CompleteClientIoctl()
sl@0: //
sl@0: // Complete outstanding PMPipeIoctl message
sl@0: //
sl@0: 	{
sl@0: 	TInt ret=KErrNone;
sl@0: 	int param=0;
sl@0: 	switch (iIoctlMessage.Int1())
sl@0: 		{
sl@0: 	case E32IONREAD:
sl@0: 		if (IsWriteable() && iStatus!=0)
sl@0: 			param=iWriteBuf.Length();
sl@0: 		else
sl@0: 			param=0;	// claim that no data is available
sl@0: 		break;
sl@0: 	case E32IOSELECT:
sl@0: 		{
sl@0: 		int mask=0;
sl@0: 		if (iStatus!=0)
sl@0: 			mask=ClientSelectMask();
sl@0: 		param=(iIoctlMessage.Int3())&mask;
sl@0: 		}
sl@0: 		break;
sl@0: 	default:
sl@0: 		ret=KErrNotSupported;
sl@0: 		break;
sl@0: 		}
sl@0: 	if (ret==KErrNone)
sl@0: 		{
sl@0: 		TPtrC8 paramReturn((const TText8*)&param,4);
sl@0: 		TRAP(ret,iIoctlMessage.WriteL(2,paramReturn,0));
sl@0: 		}
sl@0: 	iIoctlMessage.Complete(ret);
sl@0: 	iClientIoctlPending=0;
sl@0: 	}