--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/ecomm/d_comm.cpp Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,2165 @@
+// 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);
+ }
+ }