kernel/eka/drivers/ecomm/d_comm.cpp
author hgs
Thu, 10 Jun 2010 11:48:01 +0100
changeset 149 d9f1e5bfe28c
parent 0 a41df078684a
child 167 b41fc9c39ca7
permissions -rw-r--r--
201021_18

// Copyright (c) 1995-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:
// e32\drivers\ecomm\d_comm.cpp
// 
//

#include <drivers/comm.h>
#include <kernel/kern_priv.h>
#include <e32hal.h>
#include <e32uid.h>

// Logging
#define LOG_ON(x) Kern::Printf##x
#define LOG_OFF(x)
#define LOG		LOG_OFF


//#define __UART_RX_ERROR(x)    *(TUint*)0xfeedface=(x)
//#define __OVERRUN() *(TUint*)0xfaece5=0

#define __UART_RX_ERROR(x)
#define __OVERRUN()

_LIT(KLddName,"Comm");


const TUint KXoffSignal=0x80;
//
const TUint KBreaking=0x02;
const TUint KBreakPending=0x04;
//
enum TPanic
	{
	ESetConfigWhileRequestPending,
	ESetSignalsSetAndClear,
	EResetBuffers,
	ESetReceiveBufferLength,
	};

DECLARE_STANDARD_LDD()
	{
	return new DDeviceComm;
	}

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

TInt DDeviceComm::Install()
//
// Install the device driver.
//
	{
	LOG(("DDeviceComm::Install"));
	return(SetName(&KLddName));
	}

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

TInt DDeviceComm::Create(DLogicalChannelBase*& aChannel)
//
// Create a channel on the device.
//
	{
	LOG(("DDeviceComm::Create"));
	aChannel=new DChannelComm;
	return aChannel?KErrNone:KErrNoMemory;
	}

DChannelComm::DChannelComm()
//
// Constructor
//
	:	iPowerUpDfc(DChannelComm::PowerUpDfc,this,3),
		iPowerDownDfc(DChannelComm::PowerDownDfc,this,3),
		iRxDrainDfc(DChannelComm::DrainRxDfc,this,2),
		iRxCompleteDfc(DChannelComm::CompleteRxDfc,this,2),
		iTxFillDfc(DChannelComm::FillTxDfc,this,2),
		iTxCompleteDfc(DChannelComm::CompleteTxDfc,this,2),
		iTimerDfc(DChannelComm::TimerDfcFn,this,3),
		iSigNotifyDfc(DChannelComm::SigNotifyDfc,this,2),
//		iTurnaroundMinMilliSeconds(0),
//		iTurnaroundTimerRunning(EFalse),
//		iTurnaroundTransmitDelayed(EFalse),
		iTurnaroundTimer(DChannelComm::TurnaroundStartDfc, this),
		iTurnaroundDfc(DChannelComm::TurnaroundTimeout, this, 2),
		iTimer(DChannelComm::MsCallBack,this),
		iBreakDfc(DChannelComm::FinishBreakDfc, this, 2),
		iLock(TSpinLock::EOrderGenericIrqLow3)
	{
	LOG(("DChannelComm"));
//
// Setup the default config
//
	iConfig.iRate=EBps9600;
	iConfig.iDataBits=EData8;
	iConfig.iStopBits=EStop1;
	iConfig.iParity=EParityNone;
	iConfig.iFifo=EFifoEnable;
	iConfig.iHandshake=KConfigObeyCTS;
	iConfig.iParityError=KConfigParityErrorFail;
	iConfig.iSIREnable=ESIRDisable;
//	iConfig.iTerminatorCount=0;
//	iConfig.iTerminator[0]=0;
//	iConfig.iTerminator[1]=0;
//	iConfig.iTerminator[2]=0;
//	iConfig.iTerminator[3]=0;
	iConfig.iXonChar=0x11; // XON
	iConfig.iXoffChar=0x13; // XOFF
//	iConfig.iSpecialRate=0;
//	iConfig.iParityErrorChar=0;
	iRxXonChar=0xffffffff;
	iRxXoffChar=0xffffffff;
	iStatus=EOpen;
//	iFlags=0;
//	iSignals=0;
//	iFailSignals=0;
//	iHoldSignals=0;
//	iFlowControlSignals=0;
//	iAutoSignals=0;
//	iTerminatorMask[0...31]=0;
//	iShutdown=EFalse;
//	iRxCharBuf=NULL;
//	iRxErrorBuf=NULL;
//	iRxPutIndex=0;
//	iRxGetIndex=0;
//	iRxBufSize=0;
//	iFlowControlLowerThreshold=0;
//	iFlowControlUpperThreshold=0;
//	iRxDrainThreshold=0;
//	iRxBufCompleteIndex=0;
//	iInputHeld=EFalse;
//	iRxClientBufReq=NULL;
//	iRxDesPos=0;
//	iRxLength=0;
//	iRxOutstanding=EFalse;
//	iRxError=KErrNone;
//	iTxBuffer=NULL;
//	iTxPutIndex=0;
//	iTxGetIndex=0;
//	iTxBufSize=0;
//	iTxFillThreshold=0;
	iOutputHeld=0;
	iJamChar=KTxNoChar;
//	iTxDesPtr=NULL;
//	iTxDesPos=0;
//	iTxDesLength=0;
//	iTxOutstanding=EFalse;
//	iTxError=KErrNone;

//	iTimeout=10;
	iTimeout=NKern::TimerTicks(5);
	iClient=&Kern::CurrentThread();
	iClient->Open();
//	iSigNotifyMask=0;
//	iSignalsPtr=NULL;
//	iSigNotifyStatus=NULL;
	iBreakStatus=NULL;
	iNotifiedSignals=0xffffffff;
	iPinObjSetConfig=NULL;
	}

DChannelComm::~DChannelComm()
//
// Destructor
//
	{
	LOG(("~DChannelComm"));
	if (iPowerHandler)
		{
		iPowerHandler->Remove(); 
		delete iPowerHandler;
		}
    if (iRxCharBuf)
        Kern::Free(iRxCharBuf);
    if (iTxBuffer)
        Kern::Free(iTxBuffer);
	if (iBreakStatus)
		Kern::DestroyClientRequest(iBreakStatus);
	if (iSignalsReq)
		Kern::DestroyClientRequest(iSignalsReq);
	if (iPinObjSetConfig)
		Kern::DestroyVirtualPinObject(iPinObjSetConfig);
	Kern::SafeClose((DObject*&)iClient, NULL);
	}


void DChannelComm::Complete(TInt aMask, TInt aReason)
	{
	LOG(("Complete(aMask=%x aReason=%d)", aMask, aReason));
	if (aMask & ERx)
		iRxBufReq.Complete(iClient, aReason);
	if (aMask & ETx)
		iTxBufReq.Complete(iClient, aReason);
	if (aMask & ESigChg)
		Kern::QueueRequestComplete(iClient, iSignalsReq, aReason);
	if ((aMask & EBreak) && iBreakStatus && iBreakStatus->IsReady())
		Kern::QueueRequestComplete(iClient, iBreakStatus, aReason);
	}

TInt DChannelComm::Shutdown()
	{
	__KTRACE_OPT(KPOWER,Kern::Printf("DChannelComm::Shutdown()"));
	LOG(("Shutdown()"));

    if (iStatus == EActive)
        Stop(EStopPwrDown);

    Complete(EAll, KErrAbort);

	// UART interrupts are disabled; must make sure DFCs are not queued.
	iRxDrainDfc.Cancel();
	iRxCompleteDfc.Cancel();
	iTxFillDfc.Cancel();
	iTxCompleteDfc.Cancel();
	iTimer.Cancel();
	iTurnaroundTimer.Cancel();
	iTurnaroundDfc.Cancel();
	iTimerDfc.Cancel();
	iSigNotifyDfc.Cancel();
	iPowerUpDfc.Cancel();
	iPowerDownDfc.Cancel();
	iBreakTimer.Cancel();
	iBreakDfc.Cancel();
	
	if (iPdd)
		SetSignals(0,iFlowControlSignals|iAutoSignals);

	return KErrCompletion;
	}

TInt DChannelComm::DoCreate(TInt aUnit, const TDesC8* /*anInfo*/, const TVersion &aVer)
//
// Create the channel from the passed info.
//
	{
	LOG(("DoCreate(aUnit=%d,...)", aUnit));
	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));
	iPowerUpDfc.SetDfcQ(iDfcQ);
	iPowerDownDfc.SetDfcQ(iDfcQ);
	iRxDrainDfc.SetDfcQ(iDfcQ);
	iRxCompleteDfc.SetDfcQ(iDfcQ);
	iTxFillDfc.SetDfcQ(iDfcQ);
	iTxCompleteDfc.SetDfcQ(iDfcQ);
	iTimerDfc.SetDfcQ(iDfcQ);
	iSigNotifyDfc.SetDfcQ(iDfcQ);
	iTurnaroundDfc.SetDfcQ(iDfcQ);
	iBreakDfc.SetDfcQ(iDfcQ);
	iMsgQ.Receive();

	// initialise the TX buffer
	iTxBufSize=KTxBufferSize;
	iTxBuffer=(TUint8*)Kern::Alloc(iTxBufSize);
	if (!iTxBuffer)
		return KErrNoMemory;
	iTxFillThreshold=iTxBufSize>>1;

	// initialise the RX buffer
	iRxBufSize=KDefaultRxBufferSize;
	iRxCharBuf=(TUint8*)Kern::Alloc(iRxBufSize<<1);
	if (!iRxCharBuf)
		return KErrNoMemory;
	iRxErrorBuf=iRxCharBuf+iRxBufSize;
	iFlowControlLowerThreshold=iRxBufSize>>2;
	iFlowControlUpperThreshold=3*iRxBufSize>>2;
	iRxDrainThreshold=iRxBufSize>>1;

	// Create request objects
	TInt r = Kern::CreateClientDataRequest(iSignalsReq);
	if (r==KErrNone)
		r = Kern::CreateClientRequest(iBreakStatus);
	if (r==KErrNone)
		r = iRxBufReq.Create();
	if (r==KErrNone)
		r = iTxBufReq.Create();
	if (r==KErrNone)
		r = Kern::CreateVirtualPinObject(iPinObjSetConfig);
	if (r != KErrNone)
		return r;

	((DComm *)iPdd)->iLdd=this;
	PddCheckConfig(iConfig);
	iFailSignals=FailSignals(iConfig.iHandshake);
	iHoldSignals=HoldSignals(iConfig.iHandshake);
	iFlowControlSignals=FlowControlSignals(iConfig.iHandshake);
	iAutoSignals=AutoSignals(iConfig.iHandshake);

	// create the power handler
	iPowerHandler=new DCommPowerHandler(this);
	if (!iPowerHandler)
		return KErrNoMemory;
	iPowerHandler->Add();
	DoPowerUp();

	return KErrNone;
	}

TInt DChannelComm::RequestUserHandle(DThread* aThread, TOwnerType aType)
	{
	// Ensure that each channel can only be used by a single thread.
	return (aThread!=iClient) ?  KErrAccessDenied : KErrNone;
	}

void DChannelComm::MsCallBack(TAny* aPtr)
	{
	// called from ISR when timer completes
	DChannelComm *pC=(DChannelComm*)aPtr;
	pC->iTimerDfc.Add();
	}

void DChannelComm::TimerDfcFn(TAny* aPtr)
	{
	DChannelComm *pC=(DChannelComm*)aPtr;
	pC->TimerDfc();
	}

void DChannelComm::TimerDfc()
	{
	TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
	if (iRxOutstanding)
		{
		if (iRxGetIndex==iRxPutIndex)
			{
			// buffer empty after timeout period, so complete
			iRxBufCompleteIndex=iRxPutIndex;
			iRxOutstanding=EFalse;
			iRxOneOrMore=0;
			__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
			DoCompleteRx();
			return;
			}
		// buffer not empty, so drain buffer and requeue timer
		__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
		DoDrainRxBuffer(iRxPutIndex);
		return;
		}
	__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
	}

void DChannelComm::DrainRxDfc(TAny* aPtr)
	{
	DChannelComm *pC=(DChannelComm*)aPtr;
	pC->DoDrainRxBuffer(pC->iRxPutIndex);
	}

// Drain RX buffer in a DFC
void DChannelComm::DoDrainRxBuffer(TInt aEndIndex)
	{
	// if RX completion DFC is queued, leave buffer draining to it
	if (iRxCompleteDfc.Queued())
		return;

	LOG(("DoDrainRxBuffer(aEndIndex=%d) iRxDesPos=%d iRxBufReq.iLen=%d", aEndIndex, iRxDesPos, iRxBufReq.iLen));
    
	// If there's an Rx request with bytes outstanding...
	if (iRxBufReq.iBuf && iRxDesPos<iRxBufReq.iLen)
        {
        TInt space=iRxBufReq.iLen-iRxDesPos; // the amount of the client buffer left to fill
        TInt avail=aEndIndex-iRxGetIndex;	 // the amount of data in the Rx buffer to copy to the client buffer
        if (avail<0) // true if the data to drain wraps around the end of the buffer (i.e. the last byte to copy has a linear address less than that of the first byte)
            avail+=iRxBufSize;
        TInt len=Min(space,avail); // total number of bytes to drain

		// Drain up to (but not beyond) the end of the Rx buffer
        TInt len1=Min(len,iRxBufSize-iRxGetIndex);  // number of bytes to the end of the buffer
        TPtrC8 des(iRxCharBuf+iRxGetIndex,len1);

		TInt r = Kern::ThreadBufWrite(iClient, iRxBufReq.iBuf, des, iRxDesPos, KChunkShiftBy0, iClient);
        if (r != KErrNone)
            {
            iRxError=r;
            DoCompleteRx();
            return;
            }

		// Update the client buffer offset and the Rx buffer read pointer with what we've done so far
        TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
        iRxDesPos += len1;
        iRxGetIndex+=len1;
        if (iRxGetIndex>=iRxBufSize)
            iRxGetIndex-=iRxBufSize;
        __SPIN_UNLOCK_IRQRESTORE(iLock, irq);

		// If the data wraps around the end of the Rx buffer, now write out the second part
		// which starts at the beginning of the Rx buffer.
        len-=len1;
        if (len)
            {
            des.Set(iRxCharBuf,len);
			r=Kern::ThreadBufWrite(iClient, iRxBufReq.iBuf, des, iRxDesPos, KChunkShiftBy0, iClient);
            if (r != KErrNone)
                {
                iRxError=r;
                DoCompleteRx();
                return;
                }

			// Update client buffer offset and Rx buffer read offset
            irq = __SPIN_LOCK_IRQSAVE(iLock);
            iRxDesPos += len;
            iRxGetIndex+=len;
            __SPIN_UNLOCK_IRQRESTORE(iLock, irq);
            }

        // release flow control if necessary
        if (iInputHeld && RxCount()<=iFlowControlLowerThreshold)
            ReleaseFlowControl();

        // if we are doing ReadOneOrMore, start the timer
        if (iRxOneOrMore>0)
            {
            iTimer.OneShot(iTimeout);
            }
        }
    }


void DChannelComm::RxComplete()
{
	if (NKern::CurrentContext()==NKern::EInterrupt)
		iRxCompleteDfc.Add();
	else
		DoCompleteRx();			
}


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

void DChannelComm::DoCompleteRx()
	{
    LOG(("DoCompleteRx()"));
	if (iRxOneOrMore>0)
		iTimer.Cancel();
	if (iRxBufReq.iLen)
        {
        iRxOneOrMore=0;
        DoDrainRxBuffer(iRxBufCompleteIndex);
		iRxBufReq.Complete(iClient, iRxError);
		iRxDesPos=0;

        iRxError=KErrNone;
        // start Turnaround timer (got here because it received all data, timed out on a ReadOneOrMore or was terminated
        // early by FailSignals)
        RestartTurnaroundTimer();
        }
    else
        {
        Complete(ERx,KErrNone);
        // do not start Turnaround (got here on a request Data Available Notification)
        }
    }


void DChannelComm::TxComplete()
{
	if (NKern::CurrentContext()==NKern::EInterrupt)
		iTxCompleteDfc.Add(); 
	else
		DoCompleteTx();			
}


void DChannelComm::FillTxDfc(TAny* aPtr)
	{
	DChannelComm *pC=(DChannelComm*)aPtr;
	pC->DoFillTxBuffer();
	}

// Fill TX buffer in a DFC
void DChannelComm::DoFillTxBuffer()
	{
    LOG(("DFTB %d =%d",iTxDesPos,iTxBufReq.iLen));
	if (iTxBufReq.iBuf && iTxDesPos<iTxBufReq.iLen)
        {
        TInt space=iTxBufSize-TxCount()-1;
        TInt remaining=iTxBufReq.iLen-iTxDesPos;
        TInt len=Min(space,remaining);              // number of chars to transfer
        TInt len1=Min(len,iTxBufSize-iTxPutIndex);  // number of chars to wrap point
        TPtr8 des(iTxBuffer+iTxPutIndex,len1,len1);
        LOG(("DFTxB sp = %d rem = %d iOPH = %d",space, remaining,iOutputHeld));
		TInt r=Kern::ThreadBufRead(iClient, iTxBufReq.iBuf, des, iTxDesPos, KChunkShiftBy0);
        if (r != KErrNone)
            {
            iTxError=r;
            DoCompleteTx();
            return;
            }

        TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
        iTxDesPos+=len1;
        iTxPutIndex+=len1;
        if (iTxPutIndex>=iTxBufSize)
            iTxPutIndex-=iTxBufSize;
        __SPIN_UNLOCK_IRQRESTORE(iLock, irq);

        len-=len1;
        if (len)
            {
            des.Set(iTxBuffer,len,len);
			r=Kern::ThreadBufRead(iClient, iTxBufReq.iBuf, des, iTxDesPos, KChunkShiftBy0);
            if (r != KErrNone)
                {
                iTxError=r;
                DoCompleteTx();
                return;
                }

            irq = __SPIN_LOCK_IRQSAVE(iLock);
            iTxDesPos+=len;
            iTxPutIndex+=len;
            __SPIN_UNLOCK_IRQRESTORE(iLock, irq);
            }
        if (iTxDesPos==iTxBufReq.iLen)
            {
            // we have used up the client descriptor
            if (iConfig.iHandshake & KConfigWriteBufferedComplete)
                {
                iTxOutstanding=EFalse;
                DoCompleteTx();
                }
            }
        // if TX buffer not empty and not flow controlled, make sure TX is enabled
        if (iTxPutIndex!=iTxGetIndex  && (!iOutputHeld))
            {
            LOG(("Calling - DoTxBuff->ETx"));
            EnableTransmit();
            }
        }
    }

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

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

void DChannelComm::Start()
//
// Start the driver receiving.
//
	{
	LOG(("Start()"));
	if (iStatus!=EClosed)
		{
		PddConfigure(iConfig);
		PddStart();
		iStatus=EActive;
		if ((iConfig.iHandshake & KConfigSendXoff) && iJamChar>=0)
			EnableTransmit(); // Send XOn if there is one
		}
	}

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

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

void DChannelComm::AssertFlowControl()
	{
	iInputHeld=ETrue;
	SetSignals(0,iFlowControlSignals);
	if (iConfig.iHandshake&KConfigSendXoff)		// Doing input XON/XOFF
		{
		iJamChar=iConfig.iXoffChar;				// set up to send Xoff
		EnableTransmit();						// Make sure we are transmitting
		}
	}

void DChannelComm::ReleaseFlowControl()
	{
	iInputHeld=EFalse;
	SetSignals(iFlowControlSignals,0);
	if (iConfig.iHandshake&KConfigSendXoff)		// Doing input XON/XOFF
		{
		iJamChar=iConfig.iXonChar;				// set up to send Xon
		EnableTransmit();						// Make sure we are transmitting
		}
	}

TInt DChannelComm::SetRxBufferSize(TInt aSize)
//
// Set the receive buffer size.
//
	{
	LOG(("SetRxBufferSize(aSize=0x%X)", aSize));
	aSize=(aSize+3)&~3;
	TUint8 *newBuf=(TUint8*)Kern::ReAlloc(iRxCharBuf,aSize<<1);
	if (!newBuf)
		return KErrNoMemory;
	TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
	iRxCharBuf=newBuf;
	iRxErrorBuf=newBuf+aSize;
	iRxBufSize=aSize;
	iFlowControlLowerThreshold=aSize>>2;
	iFlowControlUpperThreshold=3*aSize>>2;
	iRxDrainThreshold=aSize>>1;
	__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
	ResetBuffers(EFalse);
	return KErrNone;
	}

TInt DChannelComm::TurnaroundSet(TUint aNewTurnaroundMilliSeconds)
	{
	LOG(("TurnaroundSet(val=0x%X)", aNewTurnaroundMilliSeconds));
	TInt r = KErrNone;
	iTurnaroundMinMilliSeconds = aNewTurnaroundMilliSeconds;
	return r;
	}

TBool DChannelComm::TurnaroundStopTimer()
// Stop the timer and DFC
	{
	LOG(("TurnaroundStopTimer()"));
	
	TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
	TBool result = iTurnaroundTimerRunning;
	if(result)
		iTurnaroundTimerRunning = EFalse;	
	__SPIN_UNLOCK_IRQRESTORE(iLock, irq);

	if (result)
		{
		iTurnaroundTimer.Cancel();
		iTurnaroundDfc.Cancel();
		}
	return result;
	}

TInt DChannelComm::TurnaroundClear()
// Clear any old timer and start timer based on new turnaround timer
// 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.
	{
	LOG(("TurnaroundClear()"));
	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;
        iTurnaroundTimerStartTimeValid = 3; //Just to make sure that the turnaround timer start time is not captured.
        RestartTurnaroundTimer();
		}
    else
		{
		if(TurnaroundStopTimer())
			{
			// if a write is waiting, start a DFC to run it
			TurnaroundStartDfcImplementation(EFalse);
			}
		}
	iTurnaroundMinMilliSeconds = 0;
	return r;
	}

TInt DChannelComm::RestartTurnaroundTimer()
	{
	LOG(("RestartTurnaroundTimer()"));
	TInt r=KErrNone;

	// POLICY: if timer is running from a previous read, stop it and re-start it
	TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
	TBool cancelDfcs = (iTurnaroundMinMilliSeconds > 0) && iTurnaroundTimerRunning;
	__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
	if (cancelDfcs) 
		{
		iTurnaroundTimer.Cancel();
		iTurnaroundDfc.Cancel();
		}

	// Start the timer & update driver state to reflect that the timer is running
	TInt timeout = 0;
	irq = __SPIN_LOCK_IRQSAVE(iLock);
	if(iTurnaroundMinMilliSeconds > 0)
		{
		iTurnaroundTimerRunning = ETrue;
		timeout = NKern::TimerTicks(iTurnaroundMinMilliSeconds);
		//Record the time stamp of turnaround timer start
		if(iTurnaroundTimerStartTimeValid != 3)
		    iTurnaroundTimerStartTime = NKern::TickCount();
		iTurnaroundTimerStartTimeValid = 1;
		}
	__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
	if (timeout)
		r=iTurnaroundTimer.OneShot(timeout);
	return r;
	}

void DChannelComm::TurnaroundStartDfc(TAny* aSelf)
	{
	DChannelComm* self = (DChannelComm*)aSelf;
	self->TurnaroundStartDfcImplementation(ETrue);		// in ISR so Irqs are already disabled
	}

void DChannelComm::TurnaroundStartDfcImplementation(TBool aInIsr)
	{
	LOG(("TurnaroundStartDfcImplementation(inIsr=%d)", aInIsr));
	TInt irq=0;
    if(!aInIsr)
		irq = __SPIN_LOCK_IRQSAVE(iLock);
	else 
		__SPIN_LOCK(iLock);

	iTurnaroundTimerRunning = EFalse;
	if(iTurnaroundTransmitDelayed || iTurnaroundBreakDelayed)
		{
        if(aInIsr)
			iTurnaroundDfc.Add();
		else
			{
			if(!aInIsr)
				__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
			else 
				__SPIN_UNLOCK(iLock);
			iTurnaroundDfc.Enque();
			return;
			}
		}
    if(!aInIsr)
		__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
	else 
		__SPIN_UNLOCK(iLock);
	}

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

void DChannelComm::TurnaroundTimeoutImplementation()
	{
	LOG(("TurnaroundTimeoutImplementation()"));
	TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
	
	if(iTurnaroundBreakDelayed)
		{
		iTurnaroundBreakDelayed=EFalse;
		if (iStatus==EClosed)
			{
            __SPIN_UNLOCK_IRQRESTORE(iLock, irq);
			Complete(EBreak, KErrNotReady);
			return;
			}
		else if(IsLineFail(iFailSignals))	// have signals changed in the meantime?
			{
            __SPIN_UNLOCK_IRQRESTORE(iLock, irq);
			Complete(EBreak, KErrCommsLineFail);	// protected -> changed in signals ISR
			return;
			}
		if (iTurnaroundTransmitDelayed)
			{
			//delay write by break instead of turnaround
			iBreakDelayedTx = ETrue;
			iTurnaroundTransmitDelayed=EFalse;
			}
		__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
        BreakOn();
		}
	else if(iTurnaroundTransmitDelayed)
		{
		iTurnaroundTransmitDelayed = EFalse;		// protected -> prevent reentrant ISR
		__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
		
		RestartDelayedTransmission();
		}
	else 
		__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
	}

void DChannelComm::ResetBuffers(TBool aResetTx)
//
// Reset the receive and maybe the transmit buffer.
//
	{
	LOG(("ResetBuffers(aResetTx=%d)", aResetTx));
	TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
	iRxPutIndex=0;
	iRxGetIndex=0;
	iRxBufCompleteIndex=0;
	if (aResetTx)
		{
		iTxPutIndex=0;
		iTxGetIndex=0;
		}
	__SPIN_UNLOCK_IRQRESTORE(iLock, irq);

	if (iStatus==EActive)
		ReleaseFlowControl();
	iInputHeld=EFalse;
	}

TInt DChannelComm::TransmitIsr()
//
// Return the next character to be transmitted to the ISR
//
	{
	TInt tChar=iJamChar;			// Look for control character to jam in
    if (tChar>=0)					// Control character to send
        {
		iJamChar=KTxNoChar;
		}
    else if (!iOutputHeld && iTxGetIndex!=iTxPutIndex)
        {
		// Get spinlock, disable interrupts to ensure we can reach the unlock 
		// statement. An FIQ before unlock that attempted to get lock would 
		// lead to CPU deadlock
		TInt irqstate = __SPIN_LOCK_IRQSAVE(iLock);
		
		// output not held and buffer not empty, get next char
		tChar=iTxBuffer[iTxGetIndex++];
		if (iTxGetIndex==iTxBufSize)
			iTxGetIndex=0;
			
		__SPIN_UNLOCK_IRQRESTORE(iLock, irqstate);
		}

	return tChar;
	}

void DChannelComm::ReceiveIsr(TUint* aChar, TInt aCount, TInt aXonXoff)
//
// Handle received character block from the ISR.
// aChar points to received characters, aCount=number received,
// aXonXoff=1 if XON received, -1 if XOFF received, 0 if neither
//
	{
	if (aXonXoff>0)
		{
		iOutputHeld &= ~KXoffSignal;	// Mark output ok. for XON/XOFF
		if (iOutputHeld==0)
			EnableTransmit();
		}
	else if (aXonXoff<0)
		{
		iOutputHeld |= KXoffSignal;		// Mark output held for XON/XOFF
		}
	if (aCount==0)						// if only XON or XOFF received
		return;

	// Get spinlock, disable interrupts to ensure we can reach the unlock 
	// statement. An FIQ before unlock that attempted to get lock would 
	// lead to CPU deadlock
	TInt irqstate = __SPIN_LOCK_IRQSAVE(iLock);

	TInt count = RxCount();
	iReceived++;

	// At or above the high water mark send xoff every other character
    if (count>=iFlowControlUpperThreshold && ((count&1)!=0 || aCount>1))
		AssertFlowControl();

	TUint* pE=aChar+aCount;
	TInt e=KErrNone;
	TInt i=iRxPutIndex;
	TInt g=iRxGetIndex;
	TInt s=iRxBufSize;
	g=g?g-1:s-1;
	TInt p=iRxOutstanding?-1:0;
    TInt thresh=iRxBufReq.iLen-iRxDesPos;
	while(aChar<pE)
		{
		TUint c=*aChar++;

		// Check for parity errors and replace char if so configured.
		if (c & KReceiveIsrParityError)
			{
			// Replace bad character
			if (iConfig.iParityError==KConfigParityErrorReplaceChar)
				c = c & ~(0xff|KReceiveIsrParityError) | iConfig.iParityErrorChar;
			// Ignore parity error
			if (iConfig.iParityError==KConfigParityErrorIgnore)
				c = c & ~KReceiveIsrParityError;
			}
		
		if (i!=g)
			{
			iRxCharBuf[i]=(TUint8)c;
			iRxErrorBuf[i]=(TUint8)(c>>24);

			if (c & KReceiveIsrMaskError)
				{
				__UART_RX_ERROR(c);
				if (c & KReceiveIsrOverrunError)
					e = KErrCommsOverrun;
				else if (c & KReceiveIsrBreakError)
					e = KErrCommsBreak;
				else if (c & KReceiveIsrFrameError)
					e = KErrCommsFrame;
				else if (c & KReceiveIsrParityError)
					e = KErrCommsParity;
				}
			count++;
			if (++i==s)
				i=0;
			if (p<0)
				{
				if (e || IsTerminator(TUint8(c)) || count==thresh)
					{
					// need to complete client request
					iRxError = e;
					p=i;
					}
				}
			}
		else
			{
			__OVERRUN();
			// buffer overrun, discard character
			e=KErrCommsOverrun;

			// make sure client is informed of overrun error
			iRxError=e;

			// discard remaining characters and complete
			p=i;
			break;
			}
		}
	iRxPutIndex=i;

	if (iRxOutstanding)
		{
		if (p>=0)
			{
			// need to complete client request
			iRxBufCompleteIndex=p;
			iRxOutstanding=EFalse;
            RxComplete();
			}
		else if (count>=iRxDrainThreshold)
			{
			// drain buffer but don't complete
			DrainRxBuffer();
			}
		else if (iRxOneOrMore<0)
			{
			// doing read one or more - drain the buffer
			// this will start the timer
			iRxOneOrMore=1;
			DrainRxBuffer();
			}
		}

	__SPIN_UNLOCK_IRQRESTORE(iLock, irqstate);

	if (iNotifyData)
		{
		iNotifyData=EFalse;
        RxComplete();
		}
	}

void DChannelComm::CheckTxBuffer()
	{
	// if buffer count < threshold, fill from client buffer
	TInt count=TxCount();
    if (iTxOutstanding && iTxDesPos<iTxBufReq.iLen && count<iTxFillThreshold)
		iTxFillDfc.Add();
	else if (count==0)
		{
		// TX buffer is now empty - see if we need to complete anything
		if (iTxOutstanding)
			{
            if (iTxBufReq.iLen==0)
				{
				// request was a zero-length write - complete if hardware flow control
				// is not asserted
				if ((~iSignals & iHoldSignals)==0)
					{
					iTxOutstanding=EFalse;
                    TxComplete();
					}
				}
			else
				{
				// request was normal TX - complete now if not doing early completion
				if (!(iConfig.iHandshake&KConfigWriteBufferedComplete))
					{
					iTxOutstanding=EFalse;
					TxComplete();
					}
				}
			}
		}
	}


//
// Pdd callback
//
void DChannelComm::UpdateSignals(TUint aSignals)
	{
    __KTRACE_OPT(KHARDWARE,Kern::Printf("CommSig: Upd %08x",aSignals));
    iSignals=(iSignals&~KDTEInputSignals)|(aSignals&KDTEInputSignals);
    DoSigNotify();	
	}



/**
 Handle a state change from the PDD. Called in ISR or DFC context.
 */
void DChannelComm::StateIsr(TUint aSignals)
    {
    iSignals=(iSignals&~KDTEInputSignals)|(aSignals&KDTEInputSignals);
    if (iSignalsReq->IsReady() && ((iSignals^iNotifiedSignals)&iSigNotifyMask) )
        {
        iSigNotifyDfc.Add();
        }
    if (IsLineFail(iFailSignals))
        {
        if (iRxOutstanding)
            {
            iRxError=KErrCommsLineFail;
            iRxBufCompleteIndex=iRxPutIndex;
            iRxOutstanding=EFalse;
			RxComplete();				
            }
        if (iTxOutstanding)
            {
            iTxError = KErrCommsLineFail;
            iTxOutstanding=EFalse;
			TxComplete();
			}
        }

	//
	// Now we must determine if output is to be held
	//
    TUint status = ~iSignals & iHoldSignals;
    if (iOutputHeld & KXoffSignal)
        status |= KXoffSignal;      // Leave the xon/xoff handshake bit

    LOG(("State - ISR - 0x%x",status));
    iOutputHeld=status;             // record new flow control state
    if (iTxGetIndex==iTxPutIndex)
        {
        // Tx buffer is empty
        if (iTxOutstanding && iTxBufReq.iLen==0 && (status&~KXoffSignal)==0)
            {
            // if hardware flow control released, complete zero-length write
            iTxOutstanding=EFalse;
			TxComplete();
			}
        }
    else if (status==0)
        {
        // Tx buffer not empty and flow control released, so restart transmission
        LOG(("Calling LDD:EnTx"));
        EnableTransmit();
        }
    }

// check if transmitter is flow controlled
void DChannelComm::CheckOutputHeld()
	{
	iOutputHeld=(iOutputHeld & KXoffSignal) | (~iSignals & iHoldSignals);
    LOG(("CheckOPH IOH = %d",iOutputHeld));
	}

void DChannelComm::HandleMsg(TMessageBase* aMsg)
	{

	if (iStandby)
		{ // postpone message handling to transition from standby
		iMsgHeld=ETrue;
		return;
		}

	TThreadMessage& m=*(TThreadMessage*)aMsg;
	LOG(("HandleMsg(%x a1=%x, a2=%x)", m.iValue, m.Int1(), m.Int2()));
	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
        DoRequest(~id,m.Ptr1(),m.Ptr2());
		m.Complete(KErrNone,ETrue);
		}
	else
		{
		// DoControl
		TInt r=DoControl(id,m.Ptr0(),m.Ptr1());
		m.Complete(r,ETrue);
		}
	}

void DChannelComm::DoCancel(TInt aMask)
//
// Cancel an outstanding request.
//
	{
	LOG(("DoCancel(%d)", aMask));
	if (aMask & RBusDevComm::ERequestReadCancel)
		{
		TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
		iRxOutstanding=EFalse;
		iNotifyData=EFalse;
		iRxDesPos=0;
        iRxBufReq.iLen=0;
		iRxError=KErrNone;
		iRxOneOrMore=0;
		__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
		iRxCompleteDfc.Cancel();
		iRxDrainDfc.Cancel();
		iTimer.Cancel();
		iTimerDfc.Cancel();
		Complete(ERx,KErrCancel);
		}
	if (aMask & RBusDevComm::ERequestWriteCancel)
		{
		TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
        iTurnaroundTransmitDelayed = EFalse;
        iTxPutIndex=0;
        iTxGetIndex=0;
        iTxOutstanding=EFalse;
        iTxDesPos=0;
        iTxBufReq.iLen=0;
		iTxError=KErrNone;
		__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
		iTxCompleteDfc.Cancel();
		iTxFillDfc.Cancel();
		Complete(ETx,KErrCancel);
		}
    if (aMask & RBusDevComm::ERequestNotifySignalChangeCancel)
        {
        iSigNotifyDfc.Cancel();
        Complete(ESigChg,KErrCancel);
        }
    if (aMask & RBusDevComm::ERequestBreakCancel)
		{
	 	TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
		if (iTurnaroundBreakDelayed)
			iTurnaroundBreakDelayed=EFalse;
		__SPIN_UNLOCK_IRQRESTORE(iLock, irq);

		iBreakDfc.Cancel();
		iBreakTimer.Cancel();
		FinishBreakImplementation(KErrCancel);
		}
	}

/**
 Intercept messages in client context before they are sent to the DFC queue
 */
TInt DChannelComm::SendMsg(TMessageBase* aMsg)
	{
	TInt r = KErrNone;
	TInt max;
	TInt len = 0;
	TThreadMessage* m = (TThreadMessage*)aMsg;

	// Handle ECloseMsg & Cancel
    TInt id=aMsg->iValue;
    if (id==(TInt)ECloseMsg || id==KMaxTInt)
        {
		LOG(("SendMsg(%s)", (id==KMaxTInt)?"Cancel":"ECloseMsg"));
		// do nothing cos these are handled on the DFC side
        }
	
	// Handle control messages that access user memory here in client context
    else if (id >= 0) 
		{
		TAny* a1 = m->iArg[0];
		switch (aMsg->iValue) 
			{
			case RBusDevComm::EControlConfig:
				{			
				LOG(("SendMsg(EControlConfig, %x)", a1));
				TPtrC8 cfg((const TUint8*)&iConfig,sizeof(iConfig));
				return Kern::ThreadDesWrite(iClient,a1,cfg,0,KTruncateToMaxLength,iClient);
				}
			case RBusDevComm::EControlSetConfig:
				{
				LOG(("SendMsg(EControlSetConfig, %x)", a1));
				if (AreAnyPending()) 
					; // r = ESetConfigWhileRequestPending;
				else
					r = Kern::PinVirtualMemory(iPinObjSetConfig, (TLinAddr)a1, sizeof(TCommConfigV01));
				}
				break;
			case RBusDevComm::EControlCaps:
				{
				LOG(("SendMsg(EControlCaps, %x)", a1));
				TCommCaps2 caps;
				PddCaps(caps);
				return Kern::ThreadDesWrite(iClient,a1,caps,0,KTruncateToMaxLength,iClient);
				}
			default:
				// Allow other control messages to go to DFC thread
				LOG(("SendMsg(Ctrl %d, %x)", aMsg->iValue, a1));
				break;
			}
		}


	// Handle requests
	else 
		{
		TRequestStatus* status = (TRequestStatus*)m->iArg[0];
		TAny* a1 = m->iArg[1];
		TAny* a2 = m->iArg[2];
		TInt reqNo = ~aMsg->iValue;
		TInt irq;
		switch (reqNo)
			{
			case RBusDevComm::ERequestRead:
				{
			    iNotifyData=EFalse;
				// If client has *not* provided a buffer pointer, it means they only want
				// to know when data becomes available.
				if (!a1)
					{
					irq = __SPIN_LOCK_IRQSAVE(iLock);
					TBool isEmpty = (iRxPutIndex==iRxGetIndex);
					iNotifyData = isEmpty;
					__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
					if (!isEmpty) // if Rx buffer has bytes in it we can complete the request immediately
						{
						Kern::RequestComplete(status, KErrNone);
						return KErrNone;
						}
					// Do not start the Turnaround timer as this is not a Read request but a request for Data Available notification
					LOG(("--Buf Empty--"));
					}

				// Get buffer length if one has been given
				if (a2)
					r = Kern::ThreadRawRead(iClient,a2,&len,sizeof(len)); 

				// Check the client descriptor is valid and large enough to hold the required amount of data.
				if (a1 && r==KErrNone) 
					{
					max = Kern::ThreadGetDesMaxLength(iClient, a1);
					if (max<Abs(len) || max<0)
						r = KErrGeneral; // do not start the Turnaround timer (invalid Descriptor this read never starts)
					}

				LOG(("SendMsg(ERequestRead, %x, len=%d) max=%d r=%d", a1, len, max, r));

				// Set client descriptor length to zero & set up client buffer object
				if (a1 && r==KErrNone) 
					{
					TPtrC8 p(NULL,0);
					r = Kern::ThreadDesWrite(iClient,a1,p,0,0,iClient);
					if (r == KErrNone)
						r = iRxBufReq.Setup(status, a1, len);
					}
				}
			break;


			//
			// ERequestWrite
			//
			case RBusDevComm::ERequestWrite:
				if (iStatus==EClosed)
					r = KErrNotReady;
				else if (!a1) 
					r = KErrArgument;
				else 
					r=Kern::ThreadRawRead(iClient, a2, &len, sizeof(len));
				LOG(("SendMsg(ERequestWrite, %x, len=%d) r=%d", a1, len, r));

				// Setup pending client request for this write
				if (r==KErrNone)
					r = iTxBufReq.Setup(status, a1, len);		
				break;


			//
			// ERequestBreak: a1 points to the number of microseconds to break for
			//
			case RBusDevComm::ERequestBreak:
				r = Kern::ThreadRawRead(iClient, a1, &iBreakTimeMicroSeconds, sizeof(TInt));
				if (r == KErrNone)
					r = iBreakStatus->SetStatus(status);					
				LOG(("SendMsg(ERequestBreak, %x) bktime=%d r=%d", a1, iBreakTimeMicroSeconds, r));
				break;


			//
			// ERequestNotifySignalChange:	a1 points to user-side int to receive the signals bitmask
			//								a2 points to the bitmask of signals the user is interested in
			//
			case RBusDevComm::ERequestNotifySignalChange:
				LOG(("SendMsg(ERequestNotifySignalChange, %x, %x)", a1, a2));
				if (!a1 || !a2)
					{
					r = KErrArgument;
					break;
					}
				// Setup word-sized client buffer
				r = Kern::ThreadRawRead(iClient,a2,&iSigNotifyMask,sizeof(TUint));
				irq = __SPIN_LOCK_IRQSAVE(iLock);
				if (r==KErrNone) 
					{
					r = iSignalsReq->SetStatus(status);
					if (r==KErrNone) 
						iSignalsReq->SetDestPtr(a1);
					}
				LOG(("ERequestNotifySignalChange: mask is %x, r is %d", iSigNotifyMask, r));
				__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
				break;


			// Unknown request
			default:
				LOG(("SendMsg(req %d, %x, %x)", reqNo, a1, a2));
				r = KErrNotSupported;
				break;

			}

			// If the request has an error, complete immediately
			if (r!=KErrNone)
				Kern::RequestComplete(status, r);
		}

	// Send the client request to the DFC queue unless there's been an error
	if (r==KErrNone)
		r = DLogicalChannel::SendMsg(aMsg);
	LOG(("<SendMsg ret %d", r));
	return r;

	}


/**
 Handle asynchronous requests. Called in DFC context.
 */
void DChannelComm::DoRequest(TInt aReqNo, TAny* a1, TAny* a2)
    {
	LOG(("DoRequest(%d %x %x)", aReqNo, a1, a2));

    //
    // First check if we have started
    //
    if (iStatus==EOpen)
        {
        Start();
        CheckOutputHeld();
        SetSignals(iAutoSignals,0);
        LOG(("DReq- RFC"));
        ReleaseFlowControl();
        }
    //
    // Check for a line fail
    //
    if (IsLineFail(iFailSignals))
		{
		Complete(EAll, KErrCommsLineFail);
		return;
		}	

    //
    // Now we can dispatch the async request
    //
    switch (aReqNo)
        {
        case RBusDevComm::ERequestRead:
			InitiateRead(iRxBufReq.iLen);
            break;

        case RBusDevComm::ERequestWrite:
            {
			
			// See if we need to delay the write
            TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
			iTurnaroundTransmitDelayed = iTurnaroundTimerRunning!=0;
			iBreakDelayedTx = (iFlags & KBreaking);
			__SPIN_UNLOCK_IRQRESTORE(iLock, irq);

			// If we do need to delay the write
            if (iTurnaroundTransmitDelayed || iBreakDelayedTx)
                break;
			
			//
			InitiateWrite();
            break;
            }

        case RBusDevComm::ERequestNotifySignalChange:
            iNotifiedSignals = iSignals;
			DoSigNotify();
            break;
            
        case RBusDevComm::ERequestBreak:
			if(iTurnaroundTimerRunning)
				iTurnaroundBreakDelayed = ETrue;
			else
				BreakOn();
			break;

        }
    }

/**
 Called in DFC context upon receipt of ERequestRead
 */
void DChannelComm::InitiateRead(TInt aLength)
    {
    LOG(("InitiateRead(%d)", aLength));
    iRxOutstanding=EFalse;
	iRxOneOrMore=0;

    // Complete zero-length read immediately
    if (aLength==0)
        {
		iRxBufReq.Complete(iClient, KErrNone);
        RestartTurnaroundTimer();
        return;
        }

	TBool length_negative = (aLength<0);
	if (length_negative)
		aLength = -aLength;
	iRxBufReq.iLen=aLength;

    // If the RX buffer is empty, we must wait for more data
    TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
    if (iRxPutIndex==iRxGetIndex)
        {
		if (length_negative)
            iRxOneOrMore=-1;        // -1 because timer not started
        iRxOutstanding=ETrue;
        __SPIN_UNLOCK_IRQRESTORE(iLock, irq);
        return;
        }
    __SPIN_UNLOCK_IRQRESTORE(iLock, irq);

    // RX buffer contains characters, must scan buffer and then complete
	if (length_negative)
        {
        // ReceiveOneOrMore, up to -aLength characters
        iRxOneOrMore=1;
        }
    TInt getIndex=iRxGetIndex;
    TInt count=0;
    TUint stat=0;
	TBool complete=EFalse;
    while(!complete)
        {
        while(count<aLength && getIndex!=iRxPutIndex)
            {
            if ((stat=iRxErrorBuf[getIndex])!=0 || IsTerminator(iRxCharBuf[getIndex]))
                {
                // this character will complete the request
                if (++getIndex==iRxBufSize)
                    getIndex=0;
                count++;
                complete=ETrue;
                break;
                }
            if (++getIndex==iRxBufSize)
                getIndex=0;
            count++;
            }
        if (count==aLength)
            complete=ETrue;
        if (!complete)
            {
            TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
            if (getIndex==iRxPutIndex)
                {
                // not enough chars to complete request, so set up to wait for more
                iRxOutstanding=ETrue;
                __SPIN_UNLOCK_IRQRESTORE(iLock, irq);
                if (count)
                    DoDrainRxBuffer(getIndex);
                return;
                }
            // more characters have arrived, loop again
            __SPIN_UNLOCK_IRQRESTORE(iLock, irq);
            }
        }

    // can complete request right now
    TInt e=KErrNone;
    if (stat)
        {
        stat<<=24;
        if (stat & KReceiveIsrOverrunError)
            e = KErrCommsOverrun;
        else if (stat & KReceiveIsrBreakError)
	        e = KErrCommsBreak;
        else if (stat & KReceiveIsrFrameError)
            e = KErrCommsFrame;
        else if (stat & KReceiveIsrParityError)
            e = KErrCommsParity;
        }
    if (iRxError==KErrNone)
        iRxError=e;
    iRxBufCompleteIndex=getIndex;
    DoCompleteRx();
    }

/**
 Called in DFC context to start a write or a delayed write
 */
void DChannelComm::InitiateWrite()
    {
    LOG(("InitiateWrite() len=%d", iTxBufReq.iLen));


	TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
	iTxDesPos=0;
	iTurnaroundTimerStartTime = 0;
	iTurnaroundTimerStartTimeValid = 2;
	if (~iSignals & iFailSignals)
		{
		__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
		iTxBufReq.Complete(iClient, KErrCommsLineFail);
		return;
		}
	if (iTxBufReq.iLen==0)
		{
		if (iTxPutIndex==iTxGetIndex && (~iSignals & iHoldSignals)==0)
			{
			__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
			iTxBufReq.Complete(iClient, KErrNone);
			return;
			}
		}

	iTxOutstanding=ETrue;
	__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
	if (iTxBufReq.iLen!=0)
		DoFillTxBuffer();
	}

void DChannelComm::SigNotifyDfc(TAny* aPtr)
	{
	((DChannelComm*)aPtr)->DoSigNotify();
	}

void DChannelComm::DoSigNotify()
    {
	// Atomically update iNotifiedSignals and prepare to signal
	TBool do_notify = EFalse;
    TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
    TUint orig_sig=iNotifiedSignals;
    if (iSignalsReq->IsReady() && ( iNotifiedSignals==0xffffffff || ((iSignals^iNotifiedSignals)&iSigNotifyMask) ) )
        {
        iNotifiedSignals=iSignals;
        do_notify=ETrue;
        }
    __SPIN_UNLOCK_IRQRESTORE(iLock, irq);
    __KTRACE_OPT(KHARDWARE,Kern::Printf("CommSig: Orig=%08x New %08x Mask %08x",orig_sig,iNotifiedSignals,iSigNotifyMask));
    if (do_notify)
        {
        TUint changed=iSigNotifyMask;
        if (orig_sig!=0xffffffff)
            changed&=(orig_sig^iNotifiedSignals);
        changed=(changed<<12)|(iNotifiedSignals&iSigNotifyMask);

		// Write the result back to client memory and complete the request
		__KTRACE_OPT(KHARDWARE,Kern::Printf("CommSig: Notify %08x",changed));
		LOG(("DoSigNotify: %08x",changed));
		TUint& rr = iSignalsReq->Data();
		rr = changed;
		Kern::QueueRequestComplete(iClient, iSignalsReq, KErrNone);
		}
    }


/**
 Manually read and act on signals
 */
void DChannelComm::UpdateAndProcessSignals()
    {
    TUint signals=Signals();
    TBool notify=EFalse;
    TBool complete_rx=EFalse;
    TBool complete_tx=EFalse;
    TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
    iSignals=(iSignals&~KDTEInputSignals)|(signals&KDTEInputSignals);
    if (iSignalsReq->IsReady() && ((iSignals^iNotifiedSignals)&iSigNotifyMask) )
        {
        notify=ETrue;
        }
    if (IsLineFail(iFailSignals))
        {
        if (iRxOutstanding)
            {
            iRxError=KErrCommsLineFail;
            iRxBufCompleteIndex=iRxPutIndex;
            iRxOutstanding=EFalse;
            complete_rx=ETrue;
            }
        if (iTxOutstanding)
            {
            iTxError = KErrCommsLineFail;
            iTxOutstanding=EFalse;
            complete_tx=ETrue;
            }
        }
    //
    // Now we must determine if output is to be held
    //
    TUint status = ~iSignals & iHoldSignals;
    if (iOutputHeld & KXoffSignal)
        status |= KXoffSignal;      // Leave the xon/xoff handshake bit

    iOutputHeld=status;             // record new flow control state
    if (iTxGetIndex==iTxPutIndex)
        {
        // Tx buffer is empty
        if (iTxOutstanding && iTxBufReq.iLen==0 && (status&~KXoffSignal)==0)
            {
            // if hardware flow control released, complete zero-length write
            iTxOutstanding=EFalse;
            complete_tx=ETrue;
            }
        }
    else if (status==0)
        {
        // Tx buffer not empty and flow control released, so restart transmission
        EnableTransmit();
        }
    __SPIN_UNLOCK_IRQRESTORE(iLock, irq);
    if (notify)
        DoSigNotify();
    if (complete_rx)
        DoCompleteRx();
    if (complete_tx)
        DoCompleteTx();
    }


TUint DChannelComm::FailSignals(TUint aHandshake)
	{
	TUint r=0;
	if ((aHandshake&(KConfigObeyCTS|KConfigFailCTS))==(KConfigObeyCTS|KConfigFailCTS))
		r|=KSignalCTS;
	if ((aHandshake&(KConfigObeyDSR|KConfigFailDSR))==(KConfigObeyDSR|KConfigFailDSR))
		r|=KSignalDSR;
	if ((aHandshake&(KConfigObeyDCD|KConfigFailDCD))==(KConfigObeyDCD|KConfigFailDCD))
		r|=KSignalDCD;
	return r;
	}

TUint DChannelComm::HoldSignals(TUint aHandshake)
	{
	TUint r=0;
	if (aHandshake & KConfigObeyCTS)
		r|=KSignalCTS;
	if (aHandshake & KConfigObeyDSR)
		r|=KSignalDSR;
	if (aHandshake & KConfigObeyDCD)
		r|=KSignalDCD;
	return r;
	}

TUint DChannelComm::FlowControlSignals(TUint aHandshake)
	{
	TUint r=0;
	if (!(aHandshake & KConfigFreeRTS))
		r|=KSignalRTS;
	else if (!(aHandshake & KConfigFreeDTR))
		r|=KSignalDTR;
	return r;
	}

TUint DChannelComm::AutoSignals(TUint aHandshake)
	{
	TUint r=0;
	if (!(aHandshake & KConfigFreeRTS) && !(aHandshake & KConfigFreeDTR))
		r|=KSignalDTR;
	return r;
	}

TInt DChannelComm::SetConfig(TCommConfigV01& c)
	{
	LOG(("SetConfig(...)"));
	TBool restart = EFalse;
	TBool purge = EFalse;
	TBool changeTerminators=EFalse;
	TInt irq;
	TInt r;

	if(c.iTerminatorCount>KConfigMaxTerminators)
		return KErrNotSupported;
	if ((r=ValidateConfig(c))!=KErrNone)
		return r;
	TUint failSignals=FailSignals(c.iHandshake);
	if (IsLineFail(failSignals))
		return KErrCommsLineFail;
	if (iConfig.iRate != c.iRate
		|| iConfig.iDataBits != c.iDataBits
		|| iConfig.iStopBits != c.iStopBits
		|| iConfig.iParity != c.iParity
		|| iConfig.iFifo != c.iFifo
		|| iConfig.iSpecialRate != c.iSpecialRate
		|| iConfig.iSIREnable != c.iSIREnable
		|| iConfig.iSIRSettings != c.iSIRSettings)
		{
		restart = ETrue;
		}
	else if (iConfig.iParityErrorChar != c.iParityErrorChar
		|| iConfig.iParityError != c.iParityError
		|| iConfig.iXonChar != c.iXonChar
		|| iConfig.iXoffChar != c.iXoffChar
		|| (iConfig.iHandshake&(KConfigObeyXoff|KConfigSendXoff))
			!= (c.iHandshake&(KConfigObeyXoff|KConfigSendXoff)))
		{
		purge = ETrue;
		}
	else
		{
		if (iConfig.iTerminatorCount==c.iTerminatorCount)
			{
			for (TInt i=0; i<iConfig.iTerminatorCount; i++)
				{
				if (iConfig.iTerminator[i]!=c.iTerminator[i])
					{
					changeTerminators=ETrue;
					break;
					}
				}
			}
		else
			changeTerminators=ETrue;
		if (!changeTerminators && c.iHandshake == iConfig.iHandshake)
			return r;	// nothing to do.
		}
	if (iStatus==EActive && (restart || purge))
		{
		SetSignals(0,iFlowControlSignals|iAutoSignals); // Drop RTS
		Stop(EStopNormal);
		iStatus=EOpen;
		if(purge)
			ResetBuffers(ETrue);
		iConfig=c;
		iFailSignals=failSignals;
		iHoldSignals=HoldSignals(c.iHandshake);
		iFlowControlSignals=FlowControlSignals(c.iHandshake);
		iAutoSignals=AutoSignals(c.iHandshake);
		Start();
		CheckOutputHeld();
		SetSignals(iFlowControlSignals|iAutoSignals,0); // Assert RTS
		irq = __SPIN_LOCK_IRQSAVE(iLock);
		}
	else
		{
		irq = __SPIN_LOCK_IRQSAVE(iLock);
		if(purge)
			ResetBuffers(ETrue);
		iConfig=c;
		iFailSignals=failSignals;
		iHoldSignals=HoldSignals(c.iHandshake);
		iFlowControlSignals=FlowControlSignals(c.iHandshake);
		iAutoSignals=AutoSignals(c.iHandshake);
		}
	if (iConfig.iHandshake&KConfigObeyXoff)
		{
		iRxXonChar=c.iXonChar;
		iRxXoffChar=c.iXoffChar;
		}
	else
		{
		iRxXonChar=0xffffffff;
		iRxXoffChar=0xffffffff;
		iOutputHeld&=~KXoffSignal;
		}
	__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
	if (iStatus==EActive)
		ReleaseFlowControl();

	// no request pending here, so no need to protect this against interrupts
	if (restart || purge || changeTerminators)
		{
		memclr(iTerminatorMask, 32);
		TInt i;
		for (i=0; i<iConfig.iTerminatorCount; i++)
			{
			SetTerminator(iConfig.iTerminator[i]);
			}
		}
	return r;
	}

TInt DChannelComm::DoControl(TInt aFunction, TAny* a1, TAny* a2)
//
// Sync requests.
//
	{
	LOG(("DoControl(aFunction=%d, a1=%x, a2=%x)", aFunction, a1, a2));

	TInt r=KErrNone;

	switch (aFunction)
		{
		case RBusDevComm::EControlSetConfig:
			{
			TCommConfigV01 c;
			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);
			}
			Kern::UnpinVirtualMemory(iPinObjSetConfig);
			break;

		case RBusDevComm::EControlSignals:
			{
			UpdateAndProcessSignals();
			r=iSignals;
			break;
			}
		case RBusDevComm::EControlSetSignals:
			{
			TUint set=(TUint)a1;
			TUint clear=(TUint)a2;
			if (set & clear)
;//				Kern::PanicCurrentThread(_L("D32COMM"), ESetSignalsSetAndClear);
			else
				{
				if (iStatus==EOpen)
					{
					Start();
					if (!(iConfig.iHandshake & KConfigFreeDTR) && !(clear & KSignalDTR))
						set|=KSignalDTR; // Assert DTR
					if (!(iConfig.iHandshake & KConfigFreeRTS) && !(clear & KSignalRTS))
						set|=KSignalRTS; // Assert RTS
					if (iConfig.iHandshake & KConfigSendXoff)
						iJamChar=iConfig.iXonChar;
					iInputHeld = EFalse;
					CheckOutputHeld();
					}
				__e32_atomic_axo_ord32(&iSignals, ~(clear|set), set);
				SetSignals(set,clear);
				}
			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=iRxBufSize;
			break;

		case RBusDevComm::EControlSetReceiveBufferLength:
			if (AreAnyPending())
;//				iThread->Panic(_L("D32COMM"),ESetReceiveBufferLength);
			else
				r=SetRxBufferSize((TInt)a1);
			break;
		// ***************************************

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

		case RBusDevComm::EControlSetMinTurnaroundTime:
				{
				if (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)
						{	
						 iTurnaroundTimerStartTimeValid = 1;
						 iTurnaroundTimerStartTime = NKern::TickCount();
						}
				    if(iTurnaroundTimerStartTimeValid != 2)
						TurnaroundClear();
					if(newTurnaroundMilliSeconds > 0)
						{
						r = TurnaroundSet(newTurnaroundMilliSeconds);
						}
					}
				}
			break;
		default:
			r=KErrNotSupported;
			}
		return(r);
		}

void DChannelComm::DoPowerUp()
//
// Called at switch on and upon Opening.
//
    {
	LOG(("DoPowerUp()"));
	__KTRACE_OPT(KPOWER,Kern::Printf("DChannelComm::DoPowerUp()"));

	ResetBuffers(ETrue);
	iRxOutstanding=EFalse;
	iNotifyData=EFalse;
	iTxOutstanding=EFalse;
    iTxDesPos=0;
    iFlags=0;

	// Cancel turnaround
	iTurnaroundMinMilliSeconds = 0;
	iTurnaroundTimerRunning = EFalse;
	iTurnaroundTransmitDelayed = EFalse;

	// cancel any DFCs/timers
	iRxDrainDfc.Cancel();
	iRxCompleteDfc.Cancel();
	iTxFillDfc.Cancel();
	iTxCompleteDfc.Cancel();
	iTimer.Cancel();
	iTurnaroundTimer.Cancel();
	iTurnaroundDfc.Cancel();
	iTimerDfc.Cancel();
	iSigNotifyDfc.Cancel();

	Complete(EAll, KErrAbort);
	if (!Kern::PowerGood())
		return;
	TUint hand=iConfig.iHandshake;
	if (hand&(KConfigFreeRTS|KConfigFreeDTR))
		{
		Start();
		if (!Kern::PowerGood())
			return;
		if (hand&KConfigFreeRTS)
			{
			if (iSignals&KSignalRTS)
				SetSignals(KSignalRTS,0);
			else
				SetSignals(0,KSignalRTS);
			}
		if (!Kern::PowerGood())
			return;
		if (hand&KConfigFreeDTR)
			{
			if (iSignals&KSignalDTR)
				SetSignals(KSignalDTR,0);
			else
				SetSignals(0,KSignalDTR);
			}
		CheckOutputHeld();
		}
	else
		{
		if (iStatus==EActive)
			iStatus=EOpen;
		}
	}

void DChannelComm::PowerUpDfc(TAny* aPtr)
	{
	
	DChannelComm* d = (DChannelComm*)aPtr;
	__PM_ASSERT(d->iStandby);
	if (d->iStatus != EClosed)
		d->DoPowerUp();
	else
		// There is racing Close(): driver was already closed (ECloseMsg) but the DPowerHandler was not destroyed yet.
		{}
	d->iStandby = EFalse;
	d->iPowerHandler->PowerUpDone();
	if (d->iMsgHeld)
		{
		__PM_ASSERT(d->iStatus != EClosed);
		d->iMsgHeld = EFalse;
		d->HandleMsg(d->iMsgQ.iMessage);
		}
	}

void DChannelComm::PowerDownDfc(TAny* aPtr)
	{
	DChannelComm* d = (DChannelComm*)aPtr;
	__PM_ASSERT(!d->iStandby);
	d->iStandby = ETrue;
	if (d->iStatus != EClosed)
		d->Shutdown();
	else
		// There is racing Close(): driver was already closed (ECloseMsg) but the DPowerHandler was not destroyed yet.
		{}
	d->iPowerHandler->PowerDownDone();
	}

DCommPowerHandler::DCommPowerHandler(DChannelComm* aChannel)
	:	DPowerHandler(KLddName), 
		iChannel(aChannel)
	{
	}

void DCommPowerHandler::PowerUp()
	{
	iChannel->iPowerUpDfc.Enque();
	}

void DCommPowerHandler::PowerDown(TPowerState)
	{
	iChannel->iPowerDownDfc.Enque();
	}

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

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

void DChannelComm::FinishBreakImplementation(TInt aError)
	{
	if (iStatus==EClosed)
		{
		Complete(EBreak, KErrNotReady);
		}
	else
		{
		BreakOff();
		Complete(EBreak, aError);
		}

	// re-setup transmission if needed, for writes after a break
	TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
	if (iBreakDelayedTx)
		{
		iBreakDelayedTx = EFalse;		// protected -> prevent reentrant ISR
		__SPIN_UNLOCK_IRQRESTORE(iLock, irq);

		RestartDelayedTransmission();
		}
	else
		__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
	}
void DChannelComm::RestartDelayedTransmission()
	{
	LOG(("RestartDelayedTransmission()"));
	TInt irq = __SPIN_LOCK_IRQSAVE(iLock);
	TBool completeTx=EFalse;
	
	iBreakDelayedTx = EFalse;		// protected -> prevent reentrant ISR
	__SPIN_UNLOCK_IRQRESTORE(iLock, irq);

	if (iStatus==EClosed)
		{
		irq = __SPIN_LOCK_IRQSAVE(iLock);
		iTxError = KErrNotReady;		// protected -> changed in signals ISR
		completeTx = ETrue;
		}

	else if(IsLineFail(iFailSignals))	// have signals changed in the meantime?
		{
		irq = __SPIN_LOCK_IRQSAVE(iLock);
		iTxError = KErrCommsLineFail;	// protected -> changed in signals ISR
		completeTx = ETrue;
		}

	else
		{
		InitiateWrite();
		}


	if(completeTx)
		{
		iTxError = KErrNone;
		__SPIN_UNLOCK_IRQRESTORE(iLock, irq);
		Complete(ETx, iTxError);
		}
	}