diff -r 000000000000 -r bde4ae8d615e os/boardsupport/emulator/emulatorbsp/specific/serialldd.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/boardsupport/emulator/emulatorbsp/specific/serialldd.cpp Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,871 @@ +// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// wins/specific/serialldd.cpp +// +// + +#include "winscomm.h" +#include +#include +#include + +_LIT(KLddName,"Comm"); + +const TUint KBreaking=0x02; +const TUint KBreakPending=0x04; + + +enum TPanic + { + ESetConfigWhileRequestPending, + ESetSignalsSetAndClear, + EResetBuffers, + ESetReceiveBufferLength, + }; + + +inline TUint32 SafeSwap(TUint32 aNewValue, TUint32& aWord) + { return __e32_atomic_swp_ord32(&aWord, aNewValue); } + +DECLARE_STANDARD_LDD() + { + return new DDeviceComm; + } + + +DDeviceComm::DDeviceComm() + { + iParseMask = KDeviceAllowAll; + iUnitsMask = 0xffffffff; // Leave units decision to the PDD + iVersion = TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber); + } + +TInt DDeviceComm::Install() + { + return(SetName(&KLddName)); + } + +void DDeviceComm::GetCaps(TDes8& aDes) const + { + TPckgBuf b; + b().version = TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber); + Kern::InfoCopy(aDes,b); + } + +TInt DDeviceComm::Create(DLogicalChannelBase*& aChannel) + { + aChannel = new DChannelComm; + return aChannel?KErrNone:KErrNoMemory; + } + + +DChannelComm::DChannelComm() + : + iRxCompleteDfc(DChannelComm::CompleteRxDfc,this,2), + iTxCompleteDfc(DChannelComm::CompleteTxDfc,this,2), + iRxDataAvailableDfc(DChannelComm::RxDataAvailableDfc,this,2), + iSigNotifyDfc(DChannelComm::SignalNotifyDfc,this,2), +// iBreakMinMilliSeconds(0), +// iTurnaroundTimerRunning(EFalse), +// iTurnaroundTransmitDelayed(EFalse), + iTurnaroundTimer(DChannelComm::TurnaroundStartDfc, this), + iTurnaroundDfc(DChannelComm::TurnaroundTimeout, this, 2), +// iTurnaroundTxDesPtr(0), +// iTurnaroundTxDesLength(0) + iBreakDfc(DChannelComm::FinishBreakDfc, this, 2) + { + iConfig.iRate = EBps9600; + iConfig.iDataBits = EData8; + iConfig.iStopBits = EStop1; + iConfig.iParity = EParityNone; + iConfig.iHandshake = KConfigObeyCTS; + iConfig.iParityError = KConfigParityErrorFail; + iConfig.iFifo = EFifoEnable; + iConfig.iTerminatorCount = 0; + iConfig.iXonChar = 0x11; + iConfig.iXoffChar = 0x13; + iConfig.iSIREnable = ESIRDisable; + + iTxError = KErrNone; + iRxError = KErrNone; + iRxDAError = KErrNone; + iSignalError = KErrNone; + iClientDestPtr = 0; + iClientSignalResultPtr = 0; + iClient = &Kern::CurrentThread(); + iClient->Open(); + } + + +DChannelComm::~DChannelComm() + { + Kern::SafeClose((DObject*&)iClient, NULL); + } + +void DChannelComm::Shutdown() + { + // clean-up... + if (iStatus == EActive) + Stop(EStopPwrDown); // stop PDD + + Complete(EAll, KErrAbort); + + iRxCompleteDfc.Cancel(); + iTxCompleteDfc.Cancel(); + iTurnaroundTimer.Cancel(); + iTurnaroundDfc.Cancel(); + iSigNotifyDfc.Cancel(); + iRxDataAvailableDfc.Cancel(); + iBreakTimer.Cancel(); + iBreakDfc.Cancel(); + } + +TInt DChannelComm::TurnaroundSet(TUint aNewTurnaroundMilliSeconds) + { + TInt r = KErrNone; + iTurnaroundMinMilliSeconds = aNewTurnaroundMilliSeconds; + return r; + } + +TBool DChannelComm::TurnaroundStopTimer() +// Stop the timer and DFC + { + TInt irq = 0; + irq = NKern::DisableInterrupts(1); + TBool result = iTurnaroundTimerRunning; + if(result) + { + iTurnaroundTimerRunning = EFalse; + iTurnaroundTimer.Cancel(); + iTurnaroundDfc.Cancel(); + } + NKern::RestoreInterrupts(irq); + return result; + } + +TInt DChannelComm::TurnaroundClear() +// Clear any old timer and start timer based on new turnaround value. +// Called for any change: from T > 0 to T == 0 or (T = t1 > 0) to (T = t2 > 0) +// POLICY: If a write has already been delayed, it will be started immediately if the requested +// turnaround time is elapsed else will only start after it is elapsed. + { + TInt r = KErrNone; + TUint delta = 0; + + if(iTurnaroundTimerStartTimeValid == 1) + { + //Calculate the turnaround time elapsed so far + delta = (NKern::TickCount() - iTurnaroundTimerStartTime) * NKern::TickPeriod(); + } + if(delta < iTurnaroundMicroSeconds) + { + iTurnaroundMinMilliSeconds = (iTurnaroundMicroSeconds - delta) / 1000; + TInt irq = NKern::DisableInterrupts(1); + // POLICY: if timer is running from a previous read, stop it and re-start it + if(iTurnaroundTimerRunning) + { + iTurnaroundTimer.Cancel(); + iTurnaroundDfc.Cancel(); + } + iTurnaroundTimerRunning = ETrue; + TInt timeout = NKern::TimerTicks(iTurnaroundMinMilliSeconds); + iTurnaroundTimer.OneShot(timeout); + NKern::RestoreInterrupts(irq); + } + else + { + if(TurnaroundStopTimer()) + { + // if a write is waiting, start a DFC to run it + TurnaroundStartDfcImplementation(EFalse); + } + } + iTurnaroundMinMilliSeconds = 0; + return r; + } + +void DChannelComm::TurnaroundStartDfc(TAny* aSelf) + { + DChannelComm* self = (DChannelComm*)aSelf; + self->TurnaroundStartDfcImplementation(ETrue); + } + +void DChannelComm::TurnaroundStartDfcImplementation(TBool inIsr) + { + TInt irq = 0; + if(!inIsr) + { + irq = NKern::DisableInterrupts(1); + } + iTurnaroundTimerRunning = EFalse; + if(iTurnaroundTransmitDelayed || iTurnaroundBreakDelayed) + { + if(inIsr) + iTurnaroundDfc.Add(); + else + { + NKern::RestoreInterrupts(irq); + iTurnaroundDfc.Enque(); + } + return; + } + if(!inIsr) + { + NKern::RestoreInterrupts(irq); + } + } + +void DChannelComm::TurnaroundTimeout(TAny* aSelf) + { + DChannelComm* self = (DChannelComm*)aSelf; + self->TurnaroundTimeoutImplementation(); + } + +void DChannelComm::TurnaroundTimeoutImplementation(void) + { + TInt irq = NKern::DisableInterrupts(1); + if (iTurnaroundBreakDelayed) + { + iTurnaroundBreakDelayed=EFalse; + if (iStatus==EClosed) + { + NKern::RestoreInterrupts(irq); + Complete(EBreak, KErrNotReady); + return; + } + + if (LineFail()) + { + NKern::RestoreInterrupts(irq); + Complete(EBreak, KErrCommsLineFail); + return; + } + + if (iTurnaroundTransmitDelayed) + { + //delay write by break instead of turnaround + iBreakDelayedTx = ETrue; + iBreakDelayedTxDesPtr = iTurnaroundTxDesPtr; + iBreakDelayedTxDesLength = iTurnaroundTxDesLength; + iTurnaroundTxDesPtr=0; + iTurnaroundTxDesLength=0; + iTurnaroundTransmitDelayed=EFalse; + } + NKern::RestoreInterrupts(irq); + BreakOn(); + } + else if(iTurnaroundTransmitDelayed) + { + iTurnaroundTransmitDelayed = EFalse; // protected -> prevent reentrant ISR + NKern::RestoreInterrupts(irq); + if (iStatus==EClosed) + { + iTurnaroundTxDesPtr = 0; + iTurnaroundTxDesLength = 0; + Complete(ETx,KErrNotReady); + return; + } + + // fail signals checked in the PDD + InitiateWrite(iTurnaroundTxDesPtr, iTurnaroundTxDesLength); + iTurnaroundTimerStartTime = 0; + iTurnaroundTimerStartTimeValid = 2; + iTurnaroundTxDesPtr = 0; + iTurnaroundTxDesLength = 0; + } + else + NKern::RestoreInterrupts(irq); + } + +TInt DChannelComm::DoCreate(TInt aUnit, const TDesC8* /*aInfo*/, const TVersion &aVer) + { + if(!Kern::CurrentThreadHasCapability(ECapabilityCommDD,__PLATSEC_DIAGNOSTIC_STRING("Checked by ECOMM.LDD (Comm Driver)"))) + return KErrPermissionDenied; + if (!Kern::QueryVersionSupported(TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber),aVer)) + return KErrNotSupported; + + // set up the correct DFC queue + SetDfcQ(((DComm*)iPdd)->DfcQ(aUnit)); + iRxCompleteDfc.SetDfcQ(iDfcQ); + iTxCompleteDfc.SetDfcQ(iDfcQ); + iRxDataAvailableDfc.SetDfcQ(iDfcQ); + iSigNotifyDfc.SetDfcQ(iDfcQ); + iTurnaroundDfc.SetDfcQ(iDfcQ); + iBreakDfc.SetDfcQ(iDfcQ); + iMsgQ.Receive(); + + ((DComm *)iPdd)->iLdd = this; + + //setup the initial port configuration + PddConfigure(iConfig); + + return KErrNone; + } + + + + +void DChannelComm::Start() + { + if (iStatus != EClosed) + { + PddStart(); + iStatus = EActive; + } + } + + + + +void DChannelComm::HandleMsg(TMessageBase* aMsg) + { + TThreadMessage& m = *(TThreadMessage*)aMsg; + TInt id = m.iValue; + if (id == (TInt)ECloseMsg) + { + Shutdown(); + iStatus = EClosed; + m.Complete(KErrNone, EFalse); + return; + } + else if (id == KMaxTInt) + { + // DoCancel + DoCancel(m.Int0()); + m.Complete(KErrNone, ETrue); + return; + } + + if (id < 0) + { + // DoRequest + TRequestStatus* pS = (TRequestStatus*)m.Ptr0(); + TInt r = DoRequest(~id, pS, m.Ptr1(), m.Ptr2()); + if (r != KErrNone) + Kern::RequestComplete(iClient, pS, r); + m.Complete(KErrNone, ETrue); + } + else + { + // DoControl + TInt r = DoControl(id, m.Ptr0(), m.Ptr1()); + m.Complete(r, ETrue); + } + } + + +TInt DChannelComm::DoRequest(TInt aReqNo, TRequestStatus* aStatus, TAny* a1, TAny* a2) + { + + // + // First check if we have started + // + if (iStatus == EOpen) + { + Start(); + } + + // Now we can dispatch the request + TInt r = KErrNone; + TInt len = 0; + switch (aReqNo) + { + case RBusDevComm::ERequestRead: + if (a2) + //get the size of the client data + r = Kern::ThreadRawRead(iClient, a2, &len, sizeof(len)); + if (r == KErrNone) + { + if (a1) //doing a read + { + iRxStatus = aStatus; + //start the read + InitiateRead(a1,len); + } + else //notify read data availiable + { + iRxDAStatus = aStatus; + NotifyReadDataAvailable(); + } + } + break; + + case RBusDevComm::ERequestWrite: + { + if (iStatus == EClosed) + return KErrNotReady; + if (!a1) + a1 = (TAny*)1; + r = Kern::ThreadRawRead(iClient, a2, &len, sizeof(len)); //get the length of the data to write + if (r == KErrNone) + { + iTxStatus = aStatus; + TInt irq = NKern::DisableInterrupts(1); + if(iTurnaroundTimerRunning) + { + iTurnaroundTransmitDelayed = ETrue; + iTurnaroundTxDesPtr = a1; + iTurnaroundTxDesLength = len; + NKern::RestoreInterrupts(irq); + } + else if (iFlags & KBreaking) + { + // currently breaking, delay the write + iBreakDelayedTx = ETrue; + iBreakDelayedTxDesPtr = a1; // save these as client could could start trashing them before the + iBreakDelayedTxDesLength = len; // transmission effectively starts + NKern::RestoreInterrupts(irq); + } + else + { + NKern::RestoreInterrupts(irq); + InitiateWrite(a1, len); //a1 is ptr to data to write (on client side) + iTurnaroundTimerStartTime = 0; + iTurnaroundTimerStartTimeValid = 2; + } + } + break; + } + + case RBusDevComm::ERequestNotifySignalChange: + { + //a1 has place to put the result + //a2 has the signal mask + if (!a1) + { + r = KErrArgument; + break; + } + + //start the signal request + TInt mask = 0; + r = Kern::ThreadRawRead(iClient, a2, &mask, sizeof(mask)); //get the signal mask + if (r == KErrNone) + { + iSignalStatus = aStatus; + InitiateNotifySignals(a1, mask); + } + break; + } + + case RBusDevComm::ERequestBreak: + { + r = Kern::ThreadRawRead(iClient, a1, &iBreakTimeMicroSeconds, sizeof(TInt)); //get the time to break for + if (r == KErrNone) + { + iBreakStatus=aStatus; + + // check if turnaround timer running. + TInt irq = NKern::DisableInterrupts(1); + if(iTurnaroundTimerRunning) + { + iTurnaroundBreakDelayed = ETrue; + NKern::RestoreInterrupts(irq); + } + else + { + NKern::RestoreInterrupts(irq); + BreakOn(); + } + } + break; + } + + default: + r = KErrNotSupported; + break; + + } + return r; + } + +TInt DChannelComm::SetConfig(TCommConfigV01& c) + { + iConfig = c; + PddConfigure(iConfig); + return KErrNone; + } + +TInt DChannelComm::DoControl(TInt aFunction, TAny* a1, TAny* a2) + { + + TCommConfigV01 c; + TInt r = KErrNone; + + switch (aFunction) + { + case RBusDevComm::EControlConfig: + { + //get the current configuration + TPtrC8 cfg((const TUint8*)&iConfig, sizeof(iConfig)); + r = Kern::ThreadDesWrite(iClient, a1, cfg, 0, KTruncateToMaxLength, iClient); + break; + } + + case RBusDevComm::EControlSetConfig: + { + if (AreAnyPending()) + Kern::PanicCurrentThread(_L("D32COMM"), ESetConfigWhileRequestPending); + else + { + memclr(&c, sizeof(c)); + TPtr8 cfg((TUint8*)&c, 0, sizeof(c)); + r = Kern::ThreadDesRead(iClient, a1, cfg, 0, 0); + if (r == KErrNone) + r = SetConfig(c); //set the new configuration + } + break; + } + + case RBusDevComm::EControlCaps: + { + //get capabilities + TCommCaps2 caps; + PddCaps(caps); //call ipdd->Caps + r = Kern::ThreadDesWrite(iClient, a1, caps, 0, KTruncateToMaxLength, iClient); + break; + } + + case RBusDevComm::EControlSignals: + { + r = Signals(); + break; + } + + case RBusDevComm::EControlSetSignals: + { +// if (((TUint)a1)&((TUint)a2)) //can't set and clear at same time +// { +// Kern::PanicCurrentThread(_L("D32COMM"), ESetSignalsSetAndClear); +// } +// else + { + + SetSignals((TUint)a1, (TUint)a2); + } + break; + } + + case RBusDevComm::EControlQueryReceiveBuffer: + r = RxCount(); + break; + + case RBusDevComm::EControlResetBuffers: + if (AreAnyPending()) + Kern::PanicCurrentThread(_L("D32COMM"), EResetBuffers); + else + ResetBuffers(ETrue); + break; + + case RBusDevComm::EControlReceiveBufferLength: + r = RxBufferSize(); + break; + + case RBusDevComm::EControlSetReceiveBufferLength: + if (AreAnyPending()) + Kern::PanicCurrentThread(_L("D32COMM"), ESetReceiveBufferLength); + else + r = SetRxBufferSize((TInt)a1); + break; + + case RBusDevComm::EControlMinTurnaroundTime: + r = iTurnaroundMicroSeconds; // used saved value + break; + + case RBusDevComm::EControlSetMinTurnaroundTime: + { + if ((TInt)a1<0) + a1=(TAny*)0; + iTurnaroundMicroSeconds = (TUint)a1; // save this + TUint newTurnaroundMilliSeconds = (TUint)a1/1000; // convert to ms + if(newTurnaroundMilliSeconds != iTurnaroundMinMilliSeconds) + { + // POLICY: if a new turnaround time is set before the previous running timer has expired + // then the timer is adjusted depending on the new value and if any + // write request has been queued, transmission will proceed after the timer has expired. + if(iTurnaroundTimerStartTimeValid == 0) + { + iTurnaroundTimerStartTime = NKern::TickCount(); + iTurnaroundTimerStartTimeValid = 1; + } + if(iTurnaroundTimerStartTimeValid != 2) + TurnaroundClear(); + if(newTurnaroundMilliSeconds > 0) + { + r = TurnaroundSet(newTurnaroundMilliSeconds); + } + } + } + break; + + default: + r = KErrNotSupported; + } + return(r); + } + + +void DChannelComm::SignalNotifyDfc(TAny* aPtr) + { + DChannelComm* pC = (DChannelComm*)aPtr; + pC->DoSignalNotify(); + } + +void DChannelComm::RxDataAvailableDfc(TAny* aPtr) + { + DChannelComm* pC = (DChannelComm*)aPtr; + pC->DoRxDataAvailable(); + } + +void DChannelComm::DoRxDataAvailable() + { + Complete(ERxDA, iRxDAError); + iRxDAError = KErrNone; + } + +void DChannelComm::DoSignalNotify() + { + //copy the data back to the client + if (iSignalError == KErrNone) + iSignalError = Kern::ThreadRawWrite(iClient, iClientSignalResultPtr,&iSignalResult, sizeof(iSignalResult), iClient); + Complete(ESigChg, iSignalError); + iSignalError = KErrNone; + } + +void DChannelComm::CompleteTxDfc(TAny* aPtr) + { + DChannelComm* pC = (DChannelComm*)aPtr; + pC->DoCompleteTx(); + } + +void DChannelComm::DoCompleteTx() + { + Complete(ETx, iTxError); + iTxError = KErrNone; + } + +void DChannelComm::CompleteRxDfc(TAny* aPtr) + { + DChannelComm* pC = (DChannelComm*)aPtr; + pC->DoCompleteRx(); + } + +void DChannelComm::DoCompleteRx() + { + if (iRxError == KErrNone) + { + //copy the data back to the client + iRxError = Kern::ThreadDesWrite(iClient, (TDes8*)iClientDestPtr, *RxBuffer(), 0, KChunkShiftBy0, iClient); + } + Complete(ERx, iRxError); + iRxError = KErrNone; + TInt irq = NKern::DisableInterrupts(1); + if(iTurnaroundMinMilliSeconds > 0) + { + // POLICY: if timer is running from a previous read, stop it and re-start it + if(iTurnaroundTimerRunning) + { + iTurnaroundTimer.Cancel(); + iTurnaroundDfc.Cancel(); + } + iTurnaroundTimerRunning = ETrue; + TInt timeout = NKern::TimerTicks(iTurnaroundMinMilliSeconds); + iTurnaroundTimer.OneShot(timeout); + //Record the timestamp of turnaround timer start. + iTurnaroundTimerStartTimeValid = 1; + iTurnaroundTimerStartTime = NKern::TickCount(); + } + NKern::RestoreInterrupts(irq); + } + +void DChannelComm::DoCancel(TInt aMask) + { + if (aMask & RBusDevComm::ERequestReadCancel) + { + ReadCancel(); + } + + if (aMask & RBusDevComm::ERequestWriteCancel) + { + TInt irq = NKern::DisableInterrupts(1); + if(iTurnaroundTransmitDelayed) + { + iTurnaroundTxDesPtr = 0; + iTurnaroundTxDesLength = 0; + iTurnaroundTransmitDelayed = EFalse; + } + NKern::RestoreInterrupts(irq); + + WriteCancel(); + } + + if (aMask & RBusDevComm::ERequestNotifySignalChangeCancel) + { + SignalChangeCancel(); + Complete(ESigChg,KErrCancel); + } + + if (aMask & RBusDevComm::ERequestBreakCancel) + { + TInt irq = NKern::DisableInterrupts(1); + if(iTurnaroundBreakDelayed) + iTurnaroundBreakDelayed = EFalse; + NKern::RestoreInterrupts(irq); + + iBreakDfc.Cancel(); + iBreakTimer.Cancel(); + FinishBreakImplementation(KErrCancel); + } + } + + +void DChannelComm::InitiateWrite(TAny *aTxDes, TInt aLength) + { +//aTxDes has client side data +//aLength has the len + + if (!aTxDes) + { + Complete(ETx, KErrArgument); + return; + } + // call the pdd to fill its buffer and write the data + Write(iClient, aTxDes, aLength); + } + +void DChannelComm::InitiateRead(TAny *aRxDes, TInt aLength) + { + + // Complete zero-length read immediately. maybe not + +// if (aLength == 0) +// { +// Complete(ERx, KErrNone); +// return; +// } + TInt max=Kern::ThreadGetDesMaxLength(iClient,aRxDes); + + if (max < Abs(aLength) || max < 0) + Complete(ERx, KErrGeneral); + // do not start the Turnaround timer (invalid Descriptor this read never starts) + else + { + iClientDestPtr = aRxDes; + Read(iClient, aRxDes, aLength); + } + } + +void DChannelComm::InitiateNotifySignals(TAny *aSignalResultPtr, TInt aMask) + { + //aMask has the mask of signals we require + //aSignalResultPtr is a pointer to the clients area for the result + iClientSignalResultPtr = (TUint*)aSignalResultPtr; + NotifySignals(iClient, aMask); + } + +void DChannelComm::NotifyReadDataAvailable() + { + NotifyDataAvailable(); + } + + +void DChannelComm::Complete(TInt aMask, TInt aReason) + { + if (aMask & ERx) + Kern::RequestComplete(iClient, iRxStatus, aReason); + if (aMask & ETx) + Kern::RequestComplete(iClient, iTxStatus, aReason); + if (aMask & ESigChg) + Kern::RequestComplete(iClient, iSignalStatus, aReason); + if (aMask & ERxDA) + Kern::RequestComplete(iClient, iRxDAStatus, aReason); + if (aMask & EBreak) + Kern::RequestComplete(iClient, iBreakStatus, aReason); + } + +void DChannelComm::BreakOn() +// +// Start the driver breaking. +// + { + iFlags&=(~KBreakPending); + iFlags|=KBreaking; + PddBreak(ETrue); + iBreakTimer.OneShot(iBreakTimeMicroSeconds, DChannelComm::FinishBreak, this); + } + +void DChannelComm::BreakOff() +// +// Stop the driver breaking. +// + { + PddBreak(EFalse); + iFlags&=~KBreaking; + } + +void DChannelComm::FinishBreak(TAny* aSelf) + { + DChannelComm* self = (DChannelComm*)aSelf; + self->QueueFinishBreakDfc(); + } + +void DChannelComm::FinishBreakDfc(TAny* aSelf) + { + DChannelComm* self = (DChannelComm*)aSelf; + self->FinishBreakImplementation(KErrNone); + } + +void DChannelComm::QueueFinishBreakDfc() + { + iBreakDfc.Enque(); + } + +void DChannelComm::FinishBreakImplementation(TInt aBreakError) + { + if (iStatus==EClosed) + { + Complete(EBreak, KErrNotReady); + } + else if(LineFail()) // have signals changed in the meantime? + { + Complete(EBreak, KErrCommsLineFail); + } + else + { + BreakOff(); + Complete(EBreak, aBreakError); + + TInt irq = NKern::DisableInterrupts(1); + if(iBreakDelayedTx) + { + iBreakDelayedTx = EFalse; // protected -> prevent reentrant ISR + NKern::RestoreInterrupts(irq); + if (iStatus==EClosed) + { + iBreakDelayedTxDesPtr = 0; + iBreakDelayedTxDesLength = 0; + Complete(ETx,KErrNotReady); + return; + } + + // fail signals checked in the PDD + InitiateWrite(iBreakDelayedTxDesPtr, iBreakDelayedTxDesLength); + iBreakDelayedTxDesPtr = 0; + iBreakDelayedTxDesLength = 0; + } + else + NKern::RestoreInterrupts(irq); + + } + }