diff -r 000000000000 -r 96e5fb8b040d kernel/eka/drivers/ecomm/d_comm.cpp --- /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 +#include +#include +#include + +// 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 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=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=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>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 && iTxDesPosIsReady() && ((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 (maxSetStatus(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(("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; iPanic(_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); + } + }