// Copyright (c) 2002-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:
// wins/specific/serialldd.cpp
// 
//

#include "winscomm.h"
#include <kernel/kern_priv.h>
#include <e32hal.h>
#include <e32uid.h>

_LIT(KLddName,"Comm");

const TUint KBreaking=0x02;
const TUint KBreakPending=0x04;


enum TPanic
	{
	ESetConfigWhileRequestPending,
	ESetSignalsSetAndClear,
	EResetBuffers,
	ESetReceiveBufferLength,
	};


inline TUint32 SafeSwap(TUint32 aNewValue, TUint32& aWord)
	{ return __e32_atomic_swp_ord32(&aWord, aNewValue); }

DECLARE_STANDARD_LDD()
	{
	return new DDeviceComm;
	}


DDeviceComm::DDeviceComm()
	{
	iParseMask = KDeviceAllowAll;
	iUnitsMask = 0xffffffff; // Leave units decision to the PDD
	iVersion = TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber);
	}

TInt DDeviceComm::Install()
	{
	return(SetName(&KLddName));
	}

void DDeviceComm::GetCaps(TDes8& aDes) const
	{
	TPckgBuf<TCapsDevCommV01> b;
	b().version = TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber);
	Kern::InfoCopy(aDes,b);
	}

TInt DDeviceComm::Create(DLogicalChannelBase*& aChannel)
	{
	aChannel = new DChannelComm;
	return aChannel?KErrNone:KErrNoMemory;
	}


DChannelComm::DChannelComm()
	:
		iRxCompleteDfc(DChannelComm::CompleteRxDfc,this,2),
		iTxCompleteDfc(DChannelComm::CompleteTxDfc,this,2),
		iRxDataAvailableDfc(DChannelComm::RxDataAvailableDfc,this,2),
		iSigNotifyDfc(DChannelComm::SignalNotifyDfc,this,2),
//		iBreakMinMilliSeconds(0),
//		iTurnaroundTimerRunning(EFalse),
//		iTurnaroundTransmitDelayed(EFalse),
		iTurnaroundTimer(DChannelComm::TurnaroundStartDfc, this),
		iTurnaroundDfc(DChannelComm::TurnaroundTimeout, this, 2),
//		iTurnaroundTxDesPtr(0),
//		iTurnaroundTxDesLength(0)
		iBreakDfc(DChannelComm::FinishBreakDfc, this, 2)
	{
	iConfig.iRate = EBps9600;
	iConfig.iDataBits = EData8;
	iConfig.iStopBits = EStop1;
	iConfig.iParity = EParityNone;
	iConfig.iHandshake = KConfigObeyCTS;
	iConfig.iParityError = KConfigParityErrorFail;
	iConfig.iFifo = EFifoEnable;
	iConfig.iTerminatorCount = 0;
	iConfig.iXonChar = 0x11;
	iConfig.iXoffChar = 0x13;
	iConfig.iSIREnable = ESIRDisable;

	iTxError = KErrNone;
	iRxError = KErrNone;
	iRxDAError = KErrNone;
	iSignalError = KErrNone;
	iClientDestPtr = 0;
	iClientSignalResultPtr = 0;
	iClient = &Kern::CurrentThread();
	iClient->Open();
	}


DChannelComm::~DChannelComm()
	{
	Kern::SafeClose((DObject*&)iClient, NULL);
	}

void DChannelComm::Shutdown()
	{
	// clean-up...
	if (iStatus == EActive)
		Stop(EStopPwrDown);			// stop PDD

	Complete(EAll, KErrAbort);

	iRxCompleteDfc.Cancel();
	iTxCompleteDfc.Cancel();
	iTurnaroundTimer.Cancel();
	iTurnaroundDfc.Cancel();
	iSigNotifyDfc.Cancel();
	iRxDataAvailableDfc.Cancel();
	iBreakTimer.Cancel();
	iBreakDfc.Cancel();
	}

TInt DChannelComm::TurnaroundSet(TUint aNewTurnaroundMilliSeconds)
	{
	TInt r = KErrNone;
	iTurnaroundMinMilliSeconds = aNewTurnaroundMilliSeconds;
	return r;
	}

TBool DChannelComm::TurnaroundStopTimer()
// Stop the timer and DFC
	{
	TInt irq = 0;
	irq = NKern::DisableInterrupts(1);
	TBool result = iTurnaroundTimerRunning;
	if(result)
		{
		iTurnaroundTimerRunning = EFalse;
		iTurnaroundTimer.Cancel();
		iTurnaroundDfc.Cancel();
		}
	NKern::RestoreInterrupts(irq);
	return result;
	}

TInt DChannelComm::TurnaroundClear()
// Clear any old timer and start timer based on new turnaround value.
// Called for any change: from T > 0 to T == 0 or (T = t1 > 0) to (T = t2 > 0)
// POLICY: If a write has already been delayed, it will be started immediately if the requested 
//  turnaround time is elapsed else will only start after it is elapsed.
	{
	TInt r = KErrNone;
	TUint delta = 0;

    if(iTurnaroundTimerStartTimeValid == 1)
		{
		//Calculate the turnaround time elapsed so far
		delta = (NKern::TickCount() - iTurnaroundTimerStartTime) * NKern::TickPeriod();
		}
	if(delta < iTurnaroundMicroSeconds)
		{
        iTurnaroundMinMilliSeconds = (iTurnaroundMicroSeconds - delta) / 1000;
      	TInt irq = NKern::DisableInterrupts(1);
		// POLICY: if timer is running from a previous read, stop it and re-start it
		if(iTurnaroundTimerRunning)
			{
			iTurnaroundTimer.Cancel();
			iTurnaroundDfc.Cancel();
			}
		iTurnaroundTimerRunning = ETrue;
		TInt timeout = NKern::TimerTicks(iTurnaroundMinMilliSeconds);
		iTurnaroundTimer.OneShot(timeout);
    	NKern::RestoreInterrupts(irq);
	    }
    else
		{
		if(TurnaroundStopTimer())
			{
			// if a write is waiting, start a DFC to run it
			TurnaroundStartDfcImplementation(EFalse);
			}
		}
	iTurnaroundMinMilliSeconds = 0;
	return r;
	}

void DChannelComm::TurnaroundStartDfc(TAny* aSelf)
	{
	DChannelComm* self = (DChannelComm*)aSelf;
	self->TurnaroundStartDfcImplementation(ETrue);
	}

void DChannelComm::TurnaroundStartDfcImplementation(TBool inIsr)
	{
	TInt irq = 0;
	if(!inIsr)
		{
		irq = NKern::DisableInterrupts(1);
		}
	iTurnaroundTimerRunning = EFalse;
	if(iTurnaroundTransmitDelayed || iTurnaroundBreakDelayed)
		{
		if(inIsr)
			iTurnaroundDfc.Add();
		else
			{
			NKern::RestoreInterrupts(irq);
			iTurnaroundDfc.Enque();
			}
		return;
		}
	if(!inIsr)
		{
		NKern::RestoreInterrupts(irq);
		}
	}

void DChannelComm::TurnaroundTimeout(TAny* aSelf)
	{
	DChannelComm* self = (DChannelComm*)aSelf;
	self->TurnaroundTimeoutImplementation();
	}

void DChannelComm::TurnaroundTimeoutImplementation(void)
	{
	TInt irq = NKern::DisableInterrupts(1);
	if (iTurnaroundBreakDelayed)
		{
		iTurnaroundBreakDelayed=EFalse;
		if (iStatus==EClosed)
			{
            NKern::RestoreInterrupts(irq);
			Complete(EBreak, KErrNotReady);
			return;
			}

		if (LineFail())
			{
            NKern::RestoreInterrupts(irq);
			Complete(EBreak, KErrCommsLineFail);
			return;
			}

		if (iTurnaroundTransmitDelayed)
			{
			//delay write by break instead of turnaround
			iBreakDelayedTx = ETrue;
			iBreakDelayedTxDesPtr = iTurnaroundTxDesPtr;
			iBreakDelayedTxDesLength = iTurnaroundTxDesLength;
			iTurnaroundTxDesPtr=0;
			iTurnaroundTxDesLength=0;
			iTurnaroundTransmitDelayed=EFalse;
			}
        NKern::RestoreInterrupts(irq);
		BreakOn();
		}
	else if(iTurnaroundTransmitDelayed)
		{
		iTurnaroundTransmitDelayed = EFalse;	// protected -> prevent reentrant ISR
		NKern::RestoreInterrupts(irq);
		if (iStatus==EClosed)
			{
			iTurnaroundTxDesPtr = 0;
			iTurnaroundTxDesLength = 0;
			Complete(ETx,KErrNotReady);
			return;
			}

		// fail signals checked in the PDD
		InitiateWrite(iTurnaroundTxDesPtr, iTurnaroundTxDesLength);
		iTurnaroundTimerStartTime = 0;
		iTurnaroundTimerStartTimeValid = 2;
		iTurnaroundTxDesPtr = 0;
		iTurnaroundTxDesLength = 0;
		}
	else 
		NKern::RestoreInterrupts(irq);
	}

TInt DChannelComm::DoCreate(TInt aUnit, const TDesC8* /*aInfo*/, const TVersion &aVer)
	{
	if(!Kern::CurrentThreadHasCapability(ECapabilityCommDD,__PLATSEC_DIAGNOSTIC_STRING("Checked by ECOMM.LDD (Comm Driver)")))
		return KErrPermissionDenied;
	if (!Kern::QueryVersionSupported(TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber),aVer))
		return KErrNotSupported;

	// set up the correct DFC queue
	SetDfcQ(((DComm*)iPdd)->DfcQ(aUnit));
	iRxCompleteDfc.SetDfcQ(iDfcQ);
	iTxCompleteDfc.SetDfcQ(iDfcQ);
	iRxDataAvailableDfc.SetDfcQ(iDfcQ);
	iSigNotifyDfc.SetDfcQ(iDfcQ);
	iTurnaroundDfc.SetDfcQ(iDfcQ);
	iBreakDfc.SetDfcQ(iDfcQ);
	iMsgQ.Receive();

	((DComm *)iPdd)->iLdd = this;

	//setup the initial port configuration
	PddConfigure(iConfig);
	
	return KErrNone;
	}




void DChannelComm::Start()
	{
	if (iStatus != EClosed)
		{
		PddStart();
		iStatus = EActive;
		}
	}




void DChannelComm::HandleMsg(TMessageBase* aMsg)
	{
	TThreadMessage& m = *(TThreadMessage*)aMsg;
	TInt id = m.iValue;
	if (id == (TInt)ECloseMsg)
		{
		Shutdown();
		iStatus = EClosed;
		m.Complete(KErrNone, EFalse);
		return;
		}
	else if (id == KMaxTInt)
		{
		// DoCancel
		DoCancel(m.Int0());
		m.Complete(KErrNone, ETrue);
		return;
		}

	if (id < 0)
		{
		// DoRequest
		TRequestStatus* pS = (TRequestStatus*)m.Ptr0();
		TInt r = DoRequest(~id, pS, m.Ptr1(), m.Ptr2());
		if (r != KErrNone)
			Kern::RequestComplete(iClient, pS, r);
		m.Complete(KErrNone, ETrue);
		}
	else
		{
		// DoControl
		TInt r = DoControl(id, m.Ptr0(), m.Ptr1());
		m.Complete(r, ETrue);
		}
	}


TInt DChannelComm::DoRequest(TInt aReqNo, TRequestStatus* aStatus, TAny* a1, TAny* a2)
	{

	//
	// First check if we have started
	//
	if (iStatus == EOpen)
		{
		Start();
		}

	// Now we can dispatch the request
	TInt r = KErrNone;
	TInt len = 0;
	switch (aReqNo)
		{
		case RBusDevComm::ERequestRead:
			if (a2)
				//get the size of the client data 
				r = Kern::ThreadRawRead(iClient, a2, &len, sizeof(len));
			if (r == KErrNone)
				{
				if (a1)	//doing a read
					{
					iRxStatus = aStatus;
					//start the read
					InitiateRead(a1,len);
					}
				else	//notify read data availiable
					{
					iRxDAStatus = aStatus;
					NotifyReadDataAvailable();
					}
				}
			break;
		
		case RBusDevComm::ERequestWrite:
			{
			if (iStatus == EClosed)
				return KErrNotReady;
			if (!a1)
				a1 = (TAny*)1;
			r = Kern::ThreadRawRead(iClient, a2, &len, sizeof(len));	//get the length of the data to write
			if (r == KErrNone)
				{
				iTxStatus = aStatus;
				TInt irq = NKern::DisableInterrupts(1);
				if(iTurnaroundTimerRunning)
					{
					iTurnaroundTransmitDelayed = ETrue;
					iTurnaroundTxDesPtr = a1;
					iTurnaroundTxDesLength = len;
					NKern::RestoreInterrupts(irq);
					}
				else if (iFlags & KBreaking)
					{
					// currently breaking, delay the write
					iBreakDelayedTx = ETrue;
					iBreakDelayedTxDesPtr = a1;		// save these as client could could start trashing them before the
					iBreakDelayedTxDesLength = len;	// transmission effectively starts
					NKern::RestoreInterrupts(irq);
					}
				else
					{
					NKern::RestoreInterrupts(irq);
					InitiateWrite(a1, len); //a1 is ptr to data to write (on client side)
					iTurnaroundTimerStartTime = 0;
					iTurnaroundTimerStartTimeValid = 2;
					}
				}
			break;
			}

		case RBusDevComm::ERequestNotifySignalChange:
			{
			//a1 has place to put the result
			//a2 has the signal mask
			if (!a1)
				{
				r = KErrArgument;
				break;
				}
			
			//start the signal request
			TInt mask = 0;
			r = Kern::ThreadRawRead(iClient, a2, &mask, sizeof(mask));	//get the signal mask
			if (r == KErrNone)
				{
				iSignalStatus = aStatus;
				InitiateNotifySignals(a1, mask);
				}
			break;
			}
		
		case RBusDevComm::ERequestBreak:
			{
			r = Kern::ThreadRawRead(iClient, a1, &iBreakTimeMicroSeconds, sizeof(TInt));	//get the time to break for
			if (r == KErrNone)
				{
				iBreakStatus=aStatus;
				
				// check if turnaround timer running.
				TInt irq = NKern::DisableInterrupts(1);
				if(iTurnaroundTimerRunning)
					{
					iTurnaroundBreakDelayed = ETrue;
					NKern::RestoreInterrupts(irq);
					}
				else
					{
					NKern::RestoreInterrupts(irq);
					BreakOn();
					}
				}
			break;
			}
				
		default:
			r = KErrNotSupported;
			break;

		}
	return r;
	}

TInt DChannelComm::SetConfig(TCommConfigV01& c)
	{
	iConfig = c;
	PddConfigure(iConfig);
	return KErrNone;
	}

TInt DChannelComm::DoControl(TInt aFunction, TAny* a1, TAny* a2)
	{

	TCommConfigV01 c;
	TInt r = KErrNone;

	switch (aFunction)
		{
		case RBusDevComm::EControlConfig:
			{
			//get the current configuration
			TPtrC8 cfg((const TUint8*)&iConfig, sizeof(iConfig));
			r = Kern::ThreadDesWrite(iClient, a1, cfg, 0, KTruncateToMaxLength, iClient);
			break;
			}

		case RBusDevComm::EControlSetConfig:
			{
			if (AreAnyPending())	
				Kern::PanicCurrentThread(_L("D32COMM"), ESetConfigWhileRequestPending);
			else
				{
				memclr(&c, sizeof(c));
				TPtr8 cfg((TUint8*)&c, 0, sizeof(c));
				r = Kern::ThreadDesRead(iClient, a1, cfg, 0, 0);
				if (r == KErrNone)
					r = SetConfig(c);	//set the new configuration
				}
			break;
			}

		case RBusDevComm::EControlCaps:
			{
			//get capabilities
			TCommCaps2 caps;
			PddCaps(caps);	//call ipdd->Caps
			r = Kern::ThreadDesWrite(iClient, a1, caps, 0, KTruncateToMaxLength, iClient);
			break;
			}

		case RBusDevComm::EControlSignals:
			{
			r = Signals();
			break;
			}

		case RBusDevComm::EControlSetSignals:
			{
//			if (((TUint)a1)&((TUint)a2))	//can't set and clear at same time
//				{
//				Kern::PanicCurrentThread(_L("D32COMM"), ESetSignalsSetAndClear);
//				}
//			else
				{

				SetSignals((TUint)a1, (TUint)a2);
				}
			break;
			}

		case RBusDevComm::EControlQueryReceiveBuffer:
			r = RxCount();
			break;

		case RBusDevComm::EControlResetBuffers:
			if (AreAnyPending())
				Kern::PanicCurrentThread(_L("D32COMM"), EResetBuffers);
			else
				ResetBuffers(ETrue);
			break;

		case RBusDevComm::EControlReceiveBufferLength:
			r = RxBufferSize();
			break;

		case RBusDevComm::EControlSetReceiveBufferLength:
			if (AreAnyPending())
				Kern::PanicCurrentThread(_L("D32COMM"), ESetReceiveBufferLength);
			else
				r = SetRxBufferSize((TInt)a1);
			break;

		case RBusDevComm::EControlMinTurnaroundTime:
			r = iTurnaroundMicroSeconds;			// used saved value
			break;

		case RBusDevComm::EControlSetMinTurnaroundTime:
				{
				if ((TInt)a1<0)
					a1=(TAny*)0;
				iTurnaroundMicroSeconds = (TUint)a1;			// save this
				TUint newTurnaroundMilliSeconds = (TUint)a1/1000;	// convert to ms
				if(newTurnaroundMilliSeconds != iTurnaroundMinMilliSeconds)
					{
					// POLICY: if a new turnaround time is set before the previous running timer has expired 
  					// then the timer is adjusted depending on the new value and if any
                    // write request has been queued, transmission will proceed after the timer has expired.
					if(iTurnaroundTimerStartTimeValid == 0)
						{
						iTurnaroundTimerStartTime = NKern::TickCount();
						iTurnaroundTimerStartTimeValid = 1;
						}
					if(iTurnaroundTimerStartTimeValid != 2)
						TurnaroundClear();
					if(newTurnaroundMilliSeconds > 0)
						{
						r = TurnaroundSet(newTurnaroundMilliSeconds);
						}
					}
				}
			break;

		default:
			r = KErrNotSupported;
			}
		return(r);
		}


void DChannelComm::SignalNotifyDfc(TAny* aPtr)
	{
	DChannelComm* pC = (DChannelComm*)aPtr;
	pC->DoSignalNotify();
	}

void DChannelComm::RxDataAvailableDfc(TAny* aPtr)
	{
	DChannelComm* pC = (DChannelComm*)aPtr;
	pC->DoRxDataAvailable();
	}

void DChannelComm::DoRxDataAvailable()
	{
	Complete(ERxDA, iRxDAError);
	iRxDAError = KErrNone;
	}

void DChannelComm::DoSignalNotify()
	{
	//copy the data back to the client
	if (iSignalError == KErrNone)
		iSignalError = Kern::ThreadRawWrite(iClient, iClientSignalResultPtr,&iSignalResult, sizeof(iSignalResult), iClient);
	Complete(ESigChg, iSignalError);
	iSignalError = KErrNone;
	}

void DChannelComm::CompleteTxDfc(TAny* aPtr)
	{
	DChannelComm* pC = (DChannelComm*)aPtr;
	pC->DoCompleteTx();
	}

void DChannelComm::DoCompleteTx()
	{
	Complete(ETx, iTxError);
	iTxError = KErrNone;
	}

void DChannelComm::CompleteRxDfc(TAny* aPtr)
	{
	DChannelComm* pC = (DChannelComm*)aPtr;
	pC->DoCompleteRx();
	}

void DChannelComm::DoCompleteRx()
	{
	if (iRxError == KErrNone)
		{
		//copy the data back to the client
		iRxError = Kern::ThreadDesWrite(iClient, (TDes8*)iClientDestPtr, *RxBuffer(), 0, KChunkShiftBy0, iClient);
		}
	Complete(ERx, iRxError);
	iRxError = KErrNone;
	TInt irq = NKern::DisableInterrupts(1);
	if(iTurnaroundMinMilliSeconds > 0)
		{
		// POLICY: if timer is running from a previous read, stop it and re-start it
		if(iTurnaroundTimerRunning)
			{
			iTurnaroundTimer.Cancel();
			iTurnaroundDfc.Cancel();
			}
		iTurnaroundTimerRunning = ETrue;
		TInt timeout = NKern::TimerTicks(iTurnaroundMinMilliSeconds);
		iTurnaroundTimer.OneShot(timeout);
		//Record the timestamp of turnaround timer start.
		iTurnaroundTimerStartTimeValid = 1;
		iTurnaroundTimerStartTime = NKern::TickCount();
		}
	NKern::RestoreInterrupts(irq);
	}

void DChannelComm::DoCancel(TInt aMask)
	{
	if (aMask & RBusDevComm::ERequestReadCancel)
		{
		ReadCancel();
		}

	if (aMask & RBusDevComm::ERequestWriteCancel)
		{
		TInt irq = NKern::DisableInterrupts(1);
		if(iTurnaroundTransmitDelayed)
			{
			iTurnaroundTxDesPtr = 0;
			iTurnaroundTxDesLength = 0;
			iTurnaroundTransmitDelayed = EFalse;
			}
		NKern::RestoreInterrupts(irq);

		WriteCancel();
		}

	if (aMask & RBusDevComm::ERequestNotifySignalChangeCancel)
		{
		SignalChangeCancel();
		Complete(ESigChg,KErrCancel);
		}

	if (aMask & RBusDevComm::ERequestBreakCancel)
		{
		TInt irq = NKern::DisableInterrupts(1);
		if(iTurnaroundBreakDelayed)
			iTurnaroundBreakDelayed = EFalse;
		NKern::RestoreInterrupts(irq);
		
		iBreakDfc.Cancel();
		iBreakTimer.Cancel();
		FinishBreakImplementation(KErrCancel);
		}
	}


void DChannelComm::InitiateWrite(TAny *aTxDes, TInt aLength)
	{
//aTxDes has client side data
//aLength has the len
	
	if (!aTxDes)
		{
		Complete(ETx, KErrArgument);
		return;
		}
	// call the pdd to fill its buffer and write the data
	Write(iClient, aTxDes, aLength);
	}

void DChannelComm::InitiateRead(TAny *aRxDes, TInt aLength)
	{

	// Complete zero-length read immediately.  maybe not

//	if (aLength == 0)
//		{
//		Complete(ERx, KErrNone);
//		return;
//		}
	TInt max=Kern::ThreadGetDesMaxLength(iClient,aRxDes);

	if (max < Abs(aLength) || max < 0)
		Complete(ERx, KErrGeneral);
		// do not start the Turnaround timer (invalid Descriptor this read never starts)
	else
		{
		iClientDestPtr = aRxDes;
		Read(iClient, aRxDes, aLength);
		}
	}

void DChannelComm::InitiateNotifySignals(TAny *aSignalResultPtr, TInt aMask)
	{
	//aMask has the mask of signals we require
	//aSignalResultPtr is a pointer to the clients area for the result
	iClientSignalResultPtr = (TUint*)aSignalResultPtr;
	NotifySignals(iClient, aMask);
	}

void DChannelComm::NotifyReadDataAvailable()
	{
	NotifyDataAvailable();
	}


void DChannelComm::Complete(TInt aMask, TInt aReason)
	{
	if (aMask & ERx)
		Kern::RequestComplete(iClient, iRxStatus, aReason);
	if (aMask & ETx)
		Kern::RequestComplete(iClient, iTxStatus, aReason);
	if (aMask & ESigChg)
		Kern::RequestComplete(iClient, iSignalStatus, aReason);
	if (aMask & ERxDA)
		Kern::RequestComplete(iClient, iRxDAStatus, aReason);
	if (aMask & EBreak)
		Kern::RequestComplete(iClient, iBreakStatus, aReason);
	}

void DChannelComm::BreakOn()
//
// Start the driver breaking.
//
	{
	iFlags&=(~KBreakPending);
	iFlags|=KBreaking;
	PddBreak(ETrue);
	iBreakTimer.OneShot(iBreakTimeMicroSeconds, DChannelComm::FinishBreak, this);
	}

void DChannelComm::BreakOff()
//
// Stop the driver breaking.
//
	{
	PddBreak(EFalse);
	iFlags&=~KBreaking;
	}

void DChannelComm::FinishBreak(TAny* aSelf)
	{
	DChannelComm* self = (DChannelComm*)aSelf;
	self->QueueFinishBreakDfc();
	}

void DChannelComm::FinishBreakDfc(TAny* aSelf)
	{
	DChannelComm* self = (DChannelComm*)aSelf;
	self->FinishBreakImplementation(KErrNone);
	}

void DChannelComm::QueueFinishBreakDfc()
	{
	iBreakDfc.Enque();
	}

void DChannelComm::FinishBreakImplementation(TInt aBreakError)
	{
	if (iStatus==EClosed)
		{
		Complete(EBreak, KErrNotReady);
		}
	else if(LineFail())	// have signals changed in the meantime?
		{
		Complete(EBreak, KErrCommsLineFail);
		}
	else
		{
		BreakOff();
		Complete(EBreak, aBreakError);

		TInt irq = NKern::DisableInterrupts(1);
		if(iBreakDelayedTx)
			{
			iBreakDelayedTx = EFalse;	// protected -> prevent reentrant ISR
			NKern::RestoreInterrupts(irq);
			if (iStatus==EClosed)
				{
				iBreakDelayedTxDesPtr = 0;
				iBreakDelayedTxDesLength = 0;
				Complete(ETx,KErrNotReady);
				return;
				}

			// fail signals checked in the PDD
			InitiateWrite(iBreakDelayedTxDesPtr, iBreakDelayedTxDesLength);
			iBreakDelayedTxDesPtr = 0;
			iBreakDelayedTxDesLength = 0;
			}
		else 
			NKern::RestoreInterrupts(irq);

		}
	}
