// Copyright (c) 2001-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:
// Implementation of the Comms reader and writer mixin class.
// 
//

/**
 @file
*/

#include "CSimSerComm.h"
#include "CSimPhone.h"
#include "Simlog.h"

MComm::MComm()
/**
 * Trivial constructor.
 */
	{
	iCommReader = NULL;
	iCommWriter = NULL;
	}

MComm::~MComm()
/**
 * Destructor.  Destroy the heap-based objects and close the serial ports.
 */
	{
    delete iCommReader;
	delete iCommWriter;
	iCommPort.Close();
	iCommServer.Close();
	}

void MComm::CommConstructL(TInt aReadPriority, TInt aWritePriority)
/**
 * Second phase constructor for this class.  This function creates the reader and writer
 * objects.
 * @param aReadPriority		The active scheduler priority associated with the reader class.
 * @param aWritePriority	The active scheduler priority associated with the writer class.
 */
	{
	iCommReader = new (ELeave) CCommReader(this, aReadPriority);
	iCommWriter = new (ELeave) CCommWriter(this, aWritePriority);
	};

TInt MComm::CommOpen(const TDesC& aDll, const TDesC& aName, TCommAccess aMode)
/**
 * Connect to the C32 Server, load the CSY module and open the port ready for
 * reading and writing.  If a "shared" open is requested, the function will
 * briefly attempt to exclusively open the comm port to ensure that no-one
 * is currently on the port.  This client will allow others to share the port
 * but it wants to remain in control.
 *
 * @param aDll		The CSY module name.
 * @param aName		The serial port name.
 * @param aMode		The required access mode: shared, exclusive, etc...
 * @return TInt		Standard error value.
 */
	{
	TInt err;
	if (err = iCommServer.Connect(), err!=KErrNone)
		return err;
	if (aDll.Length()>0)
		{
		if (err = iCommServer.LoadCommModule(aDll), err!=KErrNone)
			{
			iCommServer.Close();
			return err;
			}
		}
	if (err = iCommPort.Open(iCommServer, aName, ECommExclusive), err!=KErrNone)
		// if any other TSY (or other app) is using comm port, we want to return gracefully
		// with error
		{
		iCommServer.Close();
		return err;
		}
	if (aMode==ECommShared)
		{
		iCommPort.Close();
		if (err = iCommPort.Open(iCommServer, aName, aMode), err!=KErrNone)
			{
			iCommServer.Close();
			return err;
			}
		}
	return KErrNone;
	}

TInt MComm::CommOpen(const TDesC& aName, TCommAccess aMode)
/**
 * Connect to the C32 Server and open the port ready for
 * reading and writing.  If a "shared" open is requested, the function will
 * briefly attempt to exclusively open the comm port to ensure that no-one
 * is currently on the port.  This client will allow others to share the port
 * but it wants to remain in control.
 *
 * @param aName		The serial port name.
 * @param aMode		The required access mode: shared, exclusive, etc...
 * @return TInt		Standard error value.
 */
	{
	LOGDATA1("Attempting to Open Serial Port");
	TInt err;
	if (err = iCommServer.Connect(), err!=KErrNone)
		return err;
	if (err = iCommPort.Open(iCommServer, aName, ECommExclusive), err!=KErrNone)
		// if any other TSY (or other app) is using comm port, we want to return gracefully
		// with error
		{
		iCommServer.Close();
		return err;
		}
	if (aMode==ECommShared)
		{
		iCommPort.Close();
		if (err = iCommPort.Open(iCommServer, aName, aMode), err!=KErrNone)
			{
			iCommServer.Close();
			return err;
			}
		}
	LOGDATA1("Sucessfully Opened Serial Port");
	return KErrNone;
	}

void MComm::CommClose()
/**
 * Cancel any outstanding requests and close all port handles.
 */
	{
 	if (iCommReader != NULL)
 		iCommReader->Cancel();
 	
 	if (iCommWriter != NULL)
 		iCommWriter->Cancel();
	iCommPort.Close();
	iCommServer.Close();
	}

void MComm::CommWrite(const TDesC8& aDes)
/**
 * Asynchronously start to write the contents of a descriptor to the opened serial port.
 * When the write completes, the callback function CommWriteComplete will be called.
 * @param aDes		Descriptor whose contents will be written to the port.
 */
	{
	__ASSERT_ALWAYS(iCommWriter!=NULL, SimPanic(ECommWriterNotConstructed));
	iCommPort.Write(iCommWriter->StatusRef(), aDes);
	iCommWriter->Activate();
	}

void MComm::CommWriteReady()
/**
 * This function sychronously primes the serial port for writing.  The function will not
 * complete until the serial port is powered up and the handshaking lines are
 * set to allow write operations.
 */
	{
	__ASSERT_ALWAYS(iCommWriter!=NULL, SimPanic(ECommWriterNotConstructed));
	TRequestStatus aStatus;
	iCommPort.Write(aStatus, TPtrC8(NULL, 0));
	User::WaitForRequest(aStatus);
	}

void MComm::CommRead(TDes8& aDes)
/**
 * Asynchronously to read data from the opened serial port.
 * When the passed buffer is full, the read will complete, calling the CommReadComplete callback function.
 * @param aDes		Read buffer descriptor.
 */
	{
	__ASSERT_ALWAYS(iCommReader!=NULL, SimPanic(ECommReaderNotConstructed));
	iCommPort.Read(iCommReader->StatusRef(), aDes, aDes.Length());
	iCommReader->Activate();
	}

void MComm::CommReadOneOrMore(TDes8& aDes)
/**
 * Asynchronously to read data from the opened serial port.
 * As soon as a single or multiple octets have been read, the read will complete,
 * calling the CommReadComplete callback function.
 * @param aDes		Read buffer descriptor.
 */
	{
	__ASSERT_ALWAYS(iCommReader!=NULL, SimPanic(ECommReaderNotConstructed));
	iCommPort.ReadOneOrMore(iCommReader->StatusRef(), aDes);
	iCommReader->Activate();
	}

void MComm::CommReadReady()
/**
 * This function sychronously primes the serial port for reading.  The function will not
 * complete until the serial port is powered up.
 */
	{
	__ASSERT_ALWAYS(iCommReader!=NULL, SimPanic(ECommReaderNotConstructed));
	TRequestStatus aStatus;
	iCommPort.Read(aStatus,iBuf,0);
	User::WaitForRequest(aStatus);
	}

void MComm::CommCancel()
/**
 * Cancel any outstanding read or write requests.
 */
	{
	if (iCommWriter)
		iCommWriter->Cancel();
	if (iCommReader)
		iCommReader->Cancel();
	}

void MComm::CommWriteCancel()
/**
 * Cancel an outstanding write request.
 */
	{
	if (iCommWriter)
		iCommWriter->Cancel();
	}

void MComm::CommReadCancel()
/**
 * Cancel an outstanding read request.
 */
	{
	if (iCommReader)
		iCommReader->Cancel();
	}

//
// CCommWriter
//
CCommWriter::CCommWriter(MComm* aComm, TInt aPriority)
	: CActive(aPriority), iComm(aComm)
/**
 * Constructor for asynchronous writer class.
 */
	{
	__DECLARE_NAME(_S("CCommWriter"));
	CActiveScheduler::Add(this);
	}

CCommWriter::~CCommWriter()
/**
 * Destructor for writer class.
 */
	{
	Cancel();
	}

void CCommWriter::RunL()
/**
 * Active Scheduler callback function, indicating a write completion.
 */
	{
	iComm->CommWriteComplete(iStatus.Int());
	}

void CCommWriter::DoCancel()
/**
 * Cancel an outstanding write operation.
 */
	{
	iComm->iCommPort.WriteCancel();
	}

//
// CCommReader
//
CCommReader::CCommReader(MComm* aComm, TInt aPriority)
	: CActive(aPriority), iComm(aComm)
/**
 * Constructor for asynchronous reader class.
 */
	{
	__DECLARE_NAME(_S("CCommReader"));
	CActiveScheduler::Add(this);
	}

CCommReader::~CCommReader()
/**
 * Destructor for reader class.
 */
	{
	Cancel();
	}

void CCommReader::RunL()
/**
 * Active Scheduler callback function, indicating a read completion.
 */
	{
	iComm->CommReadComplete(iStatus.Int());
	}

void CCommReader::DoCancel()
/**
 * Cancel an outstanding write operation.
 */
	{
	iComm->iCommPort.ReadCancel();
	}
