sl@0: // Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32\drivers\ecomm\d_comm.cpp sl@0: // sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: // Logging sl@0: #define LOG_ON(x) Kern::Printf##x sl@0: #define LOG_OFF(x) sl@0: #define LOG LOG_OFF sl@0: sl@0: sl@0: //#define __UART_RX_ERROR(x) *(TUint*)0xfeedface=(x) sl@0: //#define __OVERRUN() *(TUint*)0xfaece5=0 sl@0: sl@0: #define __UART_RX_ERROR(x) sl@0: #define __OVERRUN() sl@0: sl@0: _LIT(KLddName,"Comm"); sl@0: sl@0: sl@0: const TUint KXoffSignal=0x80; sl@0: // sl@0: const TUint KBreaking=0x02; sl@0: const TUint KBreakPending=0x04; sl@0: // sl@0: enum TPanic sl@0: { sl@0: ESetConfigWhileRequestPending, sl@0: ESetSignalsSetAndClear, sl@0: EResetBuffers, sl@0: ESetReceiveBufferLength, sl@0: }; sl@0: sl@0: DECLARE_STANDARD_LDD() sl@0: { sl@0: return new DDeviceComm; sl@0: } sl@0: sl@0: DDeviceComm::DDeviceComm() sl@0: // sl@0: // Constructor sl@0: // sl@0: { sl@0: LOG(("DDeviceComm::DDeviceComm")); sl@0: iParseMask=KDeviceAllowAll; sl@0: iUnitsMask=0xffffffff; // Leave units decision to the PDD sl@0: iVersion=TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber); sl@0: } sl@0: sl@0: TInt DDeviceComm::Install() sl@0: // sl@0: // Install the device driver. sl@0: // sl@0: { sl@0: LOG(("DDeviceComm::Install")); sl@0: return(SetName(&KLddName)); sl@0: } sl@0: sl@0: void DDeviceComm::GetCaps(TDes8& aDes) const sl@0: // sl@0: // Return the Comm capabilities. sl@0: // sl@0: { sl@0: LOG(("DDeviceComm::GetCaps")); sl@0: TPckgBuf b; sl@0: b().version=TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber); sl@0: Kern::InfoCopy(aDes,b); sl@0: } sl@0: sl@0: TInt DDeviceComm::Create(DLogicalChannelBase*& aChannel) sl@0: // sl@0: // Create a channel on the device. sl@0: // sl@0: { sl@0: LOG(("DDeviceComm::Create")); sl@0: aChannel=new DChannelComm; sl@0: return aChannel?KErrNone:KErrNoMemory; sl@0: } sl@0: sl@0: DChannelComm::DChannelComm() sl@0: // sl@0: // Constructor sl@0: // sl@0: : iPowerUpDfc(DChannelComm::PowerUpDfc,this,3), sl@0: iPowerDownDfc(DChannelComm::PowerDownDfc,this,3), sl@0: iRxDrainDfc(DChannelComm::DrainRxDfc,this,2), sl@0: iRxCompleteDfc(DChannelComm::CompleteRxDfc,this,2), sl@0: iTxFillDfc(DChannelComm::FillTxDfc,this,2), sl@0: iTxCompleteDfc(DChannelComm::CompleteTxDfc,this,2), sl@0: iTimerDfc(DChannelComm::TimerDfcFn,this,3), sl@0: iSigNotifyDfc(DChannelComm::SigNotifyDfc,this,2), sl@0: // iTurnaroundMinMilliSeconds(0), sl@0: // iTurnaroundTimerRunning(EFalse), sl@0: // iTurnaroundTransmitDelayed(EFalse), sl@0: iTurnaroundTimer(DChannelComm::TurnaroundStartDfc, this), sl@0: iTurnaroundDfc(DChannelComm::TurnaroundTimeout, this, 2), sl@0: iTimer(DChannelComm::MsCallBack,this), sl@0: iBreakDfc(DChannelComm::FinishBreakDfc, this, 2), sl@0: iLock(TSpinLock::EOrderGenericIrqLow3) sl@0: { sl@0: LOG(("DChannelComm")); sl@0: // sl@0: // Setup the default config sl@0: // sl@0: iConfig.iRate=EBps9600; sl@0: iConfig.iDataBits=EData8; sl@0: iConfig.iStopBits=EStop1; sl@0: iConfig.iParity=EParityNone; sl@0: iConfig.iFifo=EFifoEnable; sl@0: iConfig.iHandshake=KConfigObeyCTS; sl@0: iConfig.iParityError=KConfigParityErrorFail; sl@0: iConfig.iSIREnable=ESIRDisable; sl@0: // iConfig.iTerminatorCount=0; sl@0: // iConfig.iTerminator[0]=0; sl@0: // iConfig.iTerminator[1]=0; sl@0: // iConfig.iTerminator[2]=0; sl@0: // iConfig.iTerminator[3]=0; sl@0: iConfig.iXonChar=0x11; // XON sl@0: iConfig.iXoffChar=0x13; // XOFF sl@0: // iConfig.iSpecialRate=0; sl@0: // iConfig.iParityErrorChar=0; sl@0: iRxXonChar=0xffffffff; sl@0: iRxXoffChar=0xffffffff; sl@0: iStatus=EOpen; sl@0: // iFlags=0; sl@0: // iSignals=0; sl@0: // iFailSignals=0; sl@0: // iHoldSignals=0; sl@0: // iFlowControlSignals=0; sl@0: // iAutoSignals=0; sl@0: // iTerminatorMask[0...31]=0; sl@0: // iShutdown=EFalse; sl@0: // iRxCharBuf=NULL; sl@0: // iRxErrorBuf=NULL; sl@0: // iRxPutIndex=0; sl@0: // iRxGetIndex=0; sl@0: // iRxBufSize=0; sl@0: // iFlowControlLowerThreshold=0; sl@0: // iFlowControlUpperThreshold=0; sl@0: // iRxDrainThreshold=0; sl@0: // iRxBufCompleteIndex=0; sl@0: // iInputHeld=EFalse; sl@0: // iRxClientBufReq=NULL; sl@0: // iRxDesPos=0; sl@0: // iRxLength=0; sl@0: // iRxOutstanding=EFalse; sl@0: // iRxError=KErrNone; sl@0: // iTxBuffer=NULL; sl@0: // iTxPutIndex=0; sl@0: // iTxGetIndex=0; sl@0: // iTxBufSize=0; sl@0: // iTxFillThreshold=0; sl@0: iOutputHeld=0; sl@0: iJamChar=KTxNoChar; sl@0: // iTxDesPtr=NULL; sl@0: // iTxDesPos=0; sl@0: // iTxDesLength=0; sl@0: // iTxOutstanding=EFalse; sl@0: // iTxError=KErrNone; sl@0: sl@0: // iTimeout=10; sl@0: iTimeout=NKern::TimerTicks(5); sl@0: iClient=&Kern::CurrentThread(); sl@0: iClient->Open(); sl@0: // iSigNotifyMask=0; sl@0: // iSignalsPtr=NULL; sl@0: // iSigNotifyStatus=NULL; sl@0: iBreakStatus=NULL; sl@0: iNotifiedSignals=0xffffffff; sl@0: iPinObjSetConfig=NULL; sl@0: } sl@0: sl@0: DChannelComm::~DChannelComm() sl@0: // sl@0: // Destructor sl@0: // sl@0: { sl@0: LOG(("~DChannelComm")); sl@0: if (iPowerHandler) sl@0: { sl@0: iPowerHandler->Remove(); sl@0: delete iPowerHandler; sl@0: } sl@0: if (iRxCharBuf) sl@0: Kern::Free(iRxCharBuf); sl@0: if (iTxBuffer) sl@0: Kern::Free(iTxBuffer); sl@0: if (iBreakStatus) sl@0: Kern::DestroyClientRequest(iBreakStatus); sl@0: if (iSignalsReq) sl@0: Kern::DestroyClientRequest(iSignalsReq); sl@0: if (iPinObjSetConfig) sl@0: Kern::DestroyVirtualPinObject(iPinObjSetConfig); sl@0: Kern::SafeClose((DObject*&)iClient, NULL); sl@0: } sl@0: sl@0: sl@0: void DChannelComm::Complete(TInt aMask, TInt aReason) sl@0: { sl@0: LOG(("Complete(aMask=%x aReason=%d)", aMask, aReason)); sl@0: if (aMask & ERx) sl@0: iRxBufReq.Complete(iClient, aReason); sl@0: if (aMask & ETx) sl@0: iTxBufReq.Complete(iClient, aReason); sl@0: if (aMask & ESigChg) sl@0: Kern::QueueRequestComplete(iClient, iSignalsReq, aReason); sl@0: if ((aMask & EBreak) && iBreakStatus && iBreakStatus->IsReady()) sl@0: Kern::QueueRequestComplete(iClient, iBreakStatus, aReason); sl@0: } sl@0: sl@0: TInt DChannelComm::Shutdown() sl@0: { sl@0: __KTRACE_OPT(KPOWER,Kern::Printf("DChannelComm::Shutdown()")); sl@0: LOG(("Shutdown()")); sl@0: sl@0: if (iStatus == EActive) sl@0: Stop(EStopPwrDown); sl@0: sl@0: Complete(EAll, KErrAbort); sl@0: sl@0: // UART interrupts are disabled; must make sure DFCs are not queued. sl@0: iRxDrainDfc.Cancel(); sl@0: iRxCompleteDfc.Cancel(); sl@0: iTxFillDfc.Cancel(); sl@0: iTxCompleteDfc.Cancel(); sl@0: iTimer.Cancel(); sl@0: iTurnaroundTimer.Cancel(); sl@0: iTurnaroundDfc.Cancel(); sl@0: iTimerDfc.Cancel(); sl@0: iSigNotifyDfc.Cancel(); sl@0: iPowerUpDfc.Cancel(); sl@0: iPowerDownDfc.Cancel(); sl@0: iBreakTimer.Cancel(); sl@0: iBreakDfc.Cancel(); sl@0: sl@0: if (iPdd) sl@0: SetSignals(0,iFlowControlSignals|iAutoSignals); sl@0: sl@0: return KErrCompletion; sl@0: } sl@0: sl@0: TInt DChannelComm::DoCreate(TInt aUnit, const TDesC8* /*anInfo*/, const TVersion &aVer) sl@0: // sl@0: // Create the channel from the passed info. sl@0: // sl@0: { sl@0: LOG(("DoCreate(aUnit=%d,...)", aUnit)); sl@0: if(!Kern::CurrentThreadHasCapability(ECapabilityCommDD,__PLATSEC_DIAGNOSTIC_STRING("Checked by ECOMM.LDD (Comm Driver)"))) sl@0: return KErrPermissionDenied; sl@0: if (!Kern::QueryVersionSupported(TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber),aVer)) sl@0: return KErrNotSupported; sl@0: sl@0: // set up the correct DFC queue sl@0: SetDfcQ(((DComm*)iPdd)->DfcQ(aUnit)); sl@0: iPowerUpDfc.SetDfcQ(iDfcQ); sl@0: iPowerDownDfc.SetDfcQ(iDfcQ); sl@0: iRxDrainDfc.SetDfcQ(iDfcQ); sl@0: iRxCompleteDfc.SetDfcQ(iDfcQ); sl@0: iTxFillDfc.SetDfcQ(iDfcQ); sl@0: iTxCompleteDfc.SetDfcQ(iDfcQ); sl@0: iTimerDfc.SetDfcQ(iDfcQ); sl@0: iSigNotifyDfc.SetDfcQ(iDfcQ); sl@0: iTurnaroundDfc.SetDfcQ(iDfcQ); sl@0: iBreakDfc.SetDfcQ(iDfcQ); sl@0: iMsgQ.Receive(); sl@0: sl@0: // initialise the TX buffer sl@0: iTxBufSize=KTxBufferSize; sl@0: iTxBuffer=(TUint8*)Kern::Alloc(iTxBufSize); sl@0: if (!iTxBuffer) sl@0: return KErrNoMemory; sl@0: iTxFillThreshold=iTxBufSize>>1; sl@0: sl@0: // initialise the RX buffer sl@0: iRxBufSize=KDefaultRxBufferSize; sl@0: iRxCharBuf=(TUint8*)Kern::Alloc(iRxBufSize<<1); sl@0: if (!iRxCharBuf) sl@0: return KErrNoMemory; sl@0: iRxErrorBuf=iRxCharBuf+iRxBufSize; sl@0: iFlowControlLowerThreshold=iRxBufSize>>2; sl@0: iFlowControlUpperThreshold=3*iRxBufSize>>2; sl@0: iRxDrainThreshold=iRxBufSize>>1; sl@0: sl@0: // Create request objects sl@0: TInt r = Kern::CreateClientDataRequest(iSignalsReq); sl@0: if (r==KErrNone) sl@0: r = Kern::CreateClientRequest(iBreakStatus); sl@0: if (r==KErrNone) sl@0: r = iRxBufReq.Create(); sl@0: if (r==KErrNone) sl@0: r = iTxBufReq.Create(); sl@0: if (r==KErrNone) sl@0: r = Kern::CreateVirtualPinObject(iPinObjSetConfig); sl@0: if (r != KErrNone) sl@0: return r; sl@0: sl@0: ((DComm *)iPdd)->iLdd=this; sl@0: PddCheckConfig(iConfig); sl@0: iFailSignals=FailSignals(iConfig.iHandshake); sl@0: iHoldSignals=HoldSignals(iConfig.iHandshake); sl@0: iFlowControlSignals=FlowControlSignals(iConfig.iHandshake); sl@0: iAutoSignals=AutoSignals(iConfig.iHandshake); sl@0: sl@0: // create the power handler sl@0: iPowerHandler=new DCommPowerHandler(this); sl@0: if (!iPowerHandler) sl@0: return KErrNoMemory; sl@0: iPowerHandler->Add(); sl@0: DoPowerUp(); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: TInt DChannelComm::RequestUserHandle(DThread* aThread, TOwnerType aType) sl@0: { sl@0: // Ensure that each channel can only be used by a single thread. sl@0: return (aThread!=iClient) ? KErrAccessDenied : KErrNone; sl@0: } sl@0: sl@0: void DChannelComm::MsCallBack(TAny* aPtr) sl@0: { sl@0: // called from ISR when timer completes sl@0: DChannelComm *pC=(DChannelComm*)aPtr; sl@0: pC->iTimerDfc.Add(); sl@0: } sl@0: sl@0: void DChannelComm::TimerDfcFn(TAny* aPtr) sl@0: { sl@0: DChannelComm *pC=(DChannelComm*)aPtr; sl@0: pC->TimerDfc(); sl@0: } sl@0: sl@0: void DChannelComm::TimerDfc() sl@0: { sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: if (iRxOutstanding) sl@0: { sl@0: if (iRxGetIndex==iRxPutIndex) sl@0: { sl@0: // buffer empty after timeout period, so complete sl@0: iRxBufCompleteIndex=iRxPutIndex; sl@0: iRxOutstanding=EFalse; sl@0: iRxOneOrMore=0; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: DoCompleteRx(); sl@0: return; sl@0: } sl@0: // buffer not empty, so drain buffer and requeue timer sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: DoDrainRxBuffer(iRxPutIndex); sl@0: return; sl@0: } sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: } sl@0: sl@0: void DChannelComm::DrainRxDfc(TAny* aPtr) sl@0: { sl@0: DChannelComm *pC=(DChannelComm*)aPtr; sl@0: pC->DoDrainRxBuffer(pC->iRxPutIndex); sl@0: } sl@0: sl@0: // Drain RX buffer in a DFC sl@0: void DChannelComm::DoDrainRxBuffer(TInt aEndIndex) sl@0: { sl@0: // if RX completion DFC is queued, leave buffer draining to it sl@0: if (iRxCompleteDfc.Queued()) sl@0: return; sl@0: sl@0: LOG(("DoDrainRxBuffer(aEndIndex=%d) iRxDesPos=%d iRxBufReq.iLen=%d", aEndIndex, iRxDesPos, iRxBufReq.iLen)); sl@0: sl@0: // If there's an Rx request with bytes outstanding... sl@0: if (iRxBufReq.iBuf && iRxDesPos=iRxBufSize) sl@0: iRxGetIndex-=iRxBufSize; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: sl@0: // If the data wraps around the end of the Rx buffer, now write out the second part sl@0: // which starts at the beginning of the Rx buffer. sl@0: len-=len1; sl@0: if (len) sl@0: { sl@0: des.Set(iRxCharBuf,len); sl@0: r=Kern::ThreadBufWrite(iClient, iRxBufReq.iBuf, des, iRxDesPos, KChunkShiftBy0, iClient); sl@0: if (r != KErrNone) sl@0: { sl@0: iRxError=r; sl@0: DoCompleteRx(); sl@0: return; sl@0: } sl@0: sl@0: // Update client buffer offset and Rx buffer read offset sl@0: irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: iRxDesPos += len; sl@0: iRxGetIndex+=len; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: } sl@0: sl@0: // release flow control if necessary sl@0: if (iInputHeld && RxCount()<=iFlowControlLowerThreshold) sl@0: ReleaseFlowControl(); sl@0: sl@0: // if we are doing ReadOneOrMore, start the timer sl@0: if (iRxOneOrMore>0) sl@0: { sl@0: iTimer.OneShot(iTimeout); sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: void DChannelComm::RxComplete() sl@0: { sl@0: if (NKern::CurrentContext()==NKern::EInterrupt) sl@0: iRxCompleteDfc.Add(); sl@0: else sl@0: DoCompleteRx(); sl@0: } sl@0: sl@0: sl@0: void DChannelComm::CompleteRxDfc(TAny* aPtr) sl@0: { sl@0: DChannelComm *pC=(DChannelComm*)aPtr; sl@0: pC->DoCompleteRx(); sl@0: } sl@0: sl@0: void DChannelComm::DoCompleteRx() sl@0: { sl@0: LOG(("DoCompleteRx()")); sl@0: if (iRxOneOrMore>0) sl@0: iTimer.Cancel(); sl@0: if (iRxBufReq.iLen) sl@0: { sl@0: iRxOneOrMore=0; sl@0: DoDrainRxBuffer(iRxBufCompleteIndex); sl@0: iRxBufReq.Complete(iClient, iRxError); sl@0: iRxDesPos=0; sl@0: sl@0: iRxError=KErrNone; sl@0: // start Turnaround timer (got here because it received all data, timed out on a ReadOneOrMore or was terminated sl@0: // early by FailSignals) sl@0: RestartTurnaroundTimer(); sl@0: } sl@0: else sl@0: { sl@0: Complete(ERx,KErrNone); sl@0: // do not start Turnaround (got here on a request Data Available Notification) sl@0: } sl@0: } sl@0: sl@0: sl@0: void DChannelComm::TxComplete() sl@0: { sl@0: if (NKern::CurrentContext()==NKern::EInterrupt) sl@0: iTxCompleteDfc.Add(); sl@0: else sl@0: DoCompleteTx(); sl@0: } sl@0: sl@0: sl@0: void DChannelComm::FillTxDfc(TAny* aPtr) sl@0: { sl@0: DChannelComm *pC=(DChannelComm*)aPtr; sl@0: pC->DoFillTxBuffer(); sl@0: } sl@0: sl@0: // Fill TX buffer in a DFC sl@0: void DChannelComm::DoFillTxBuffer() sl@0: { sl@0: LOG(("DFTB %d =%d",iTxDesPos,iTxBufReq.iLen)); sl@0: if (iTxBufReq.iBuf && iTxDesPos=iTxBufSize) sl@0: iTxPutIndex-=iTxBufSize; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: sl@0: len-=len1; sl@0: if (len) sl@0: { sl@0: des.Set(iTxBuffer,len,len); sl@0: r=Kern::ThreadBufRead(iClient, iTxBufReq.iBuf, des, iTxDesPos, KChunkShiftBy0); sl@0: if (r != KErrNone) sl@0: { sl@0: iTxError=r; sl@0: DoCompleteTx(); sl@0: return; sl@0: } sl@0: sl@0: irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: iTxDesPos+=len; sl@0: iTxPutIndex+=len; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: } sl@0: if (iTxDesPos==iTxBufReq.iLen) sl@0: { sl@0: // we have used up the client descriptor sl@0: if (iConfig.iHandshake & KConfigWriteBufferedComplete) sl@0: { sl@0: iTxOutstanding=EFalse; sl@0: DoCompleteTx(); sl@0: } sl@0: } sl@0: // if TX buffer not empty and not flow controlled, make sure TX is enabled sl@0: if (iTxPutIndex!=iTxGetIndex && (!iOutputHeld)) sl@0: { sl@0: LOG(("Calling - DoTxBuff->ETx")); sl@0: EnableTransmit(); sl@0: } sl@0: } sl@0: } sl@0: sl@0: void DChannelComm::CompleteTxDfc(TAny* aPtr) sl@0: { sl@0: DChannelComm *pC=(DChannelComm*)aPtr; sl@0: pC->DoCompleteTx(); sl@0: } sl@0: sl@0: void DChannelComm::DoCompleteTx() sl@0: { sl@0: Complete(ETx,iTxError); sl@0: iTxError=KErrNone; sl@0: } sl@0: sl@0: void DChannelComm::Start() sl@0: // sl@0: // Start the driver receiving. sl@0: // sl@0: { sl@0: LOG(("Start()")); sl@0: if (iStatus!=EClosed) sl@0: { sl@0: PddConfigure(iConfig); sl@0: PddStart(); sl@0: iStatus=EActive; sl@0: if ((iConfig.iHandshake & KConfigSendXoff) && iJamChar>=0) sl@0: EnableTransmit(); // Send XOn if there is one sl@0: } sl@0: } sl@0: sl@0: void DChannelComm::BreakOn() sl@0: // sl@0: // Start the driver breaking. sl@0: // sl@0: { sl@0: LOG(("BreakOn()")); sl@0: iFlags&=(~KBreakPending); sl@0: iFlags|=KBreaking; sl@0: PddBreak(ETrue); sl@0: iBreakTimer.OneShot(iBreakTimeMicroSeconds, DChannelComm::FinishBreak, this); sl@0: } sl@0: sl@0: void DChannelComm::BreakOff() sl@0: // sl@0: // Stop the driver breaking. sl@0: // sl@0: { sl@0: LOG(("BreakOff()")); sl@0: PddBreak(EFalse); sl@0: iFlags&=(~(KBreakPending|KBreaking)); sl@0: } sl@0: sl@0: void DChannelComm::AssertFlowControl() sl@0: { sl@0: iInputHeld=ETrue; sl@0: SetSignals(0,iFlowControlSignals); sl@0: if (iConfig.iHandshake&KConfigSendXoff) // Doing input XON/XOFF sl@0: { sl@0: iJamChar=iConfig.iXoffChar; // set up to send Xoff sl@0: EnableTransmit(); // Make sure we are transmitting sl@0: } sl@0: } sl@0: sl@0: void DChannelComm::ReleaseFlowControl() sl@0: { sl@0: iInputHeld=EFalse; sl@0: SetSignals(iFlowControlSignals,0); sl@0: if (iConfig.iHandshake&KConfigSendXoff) // Doing input XON/XOFF sl@0: { sl@0: iJamChar=iConfig.iXonChar; // set up to send Xon sl@0: EnableTransmit(); // Make sure we are transmitting sl@0: } sl@0: } sl@0: sl@0: TInt DChannelComm::SetRxBufferSize(TInt aSize) sl@0: // sl@0: // Set the receive buffer size. sl@0: // sl@0: { sl@0: LOG(("SetRxBufferSize(aSize=0x%X)", aSize)); sl@0: aSize=(aSize+3)&~3; sl@0: TUint8 *newBuf=(TUint8*)Kern::ReAlloc(iRxCharBuf,aSize<<1); sl@0: if (!newBuf) sl@0: return KErrNoMemory; sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: iRxCharBuf=newBuf; sl@0: iRxErrorBuf=newBuf+aSize; sl@0: iRxBufSize=aSize; sl@0: iFlowControlLowerThreshold=aSize>>2; sl@0: iFlowControlUpperThreshold=3*aSize>>2; sl@0: iRxDrainThreshold=aSize>>1; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: ResetBuffers(EFalse); sl@0: return KErrNone; sl@0: } sl@0: sl@0: TInt DChannelComm::TurnaroundSet(TUint aNewTurnaroundMilliSeconds) sl@0: { sl@0: LOG(("TurnaroundSet(val=0x%X)", aNewTurnaroundMilliSeconds)); sl@0: TInt r = KErrNone; sl@0: iTurnaroundMinMilliSeconds = aNewTurnaroundMilliSeconds; sl@0: return r; sl@0: } sl@0: sl@0: TBool DChannelComm::TurnaroundStopTimer() sl@0: // Stop the timer and DFC sl@0: { sl@0: LOG(("TurnaroundStopTimer()")); sl@0: sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: TBool result = iTurnaroundTimerRunning; sl@0: if(result) sl@0: iTurnaroundTimerRunning = EFalse; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: sl@0: if (result) sl@0: { sl@0: iTurnaroundTimer.Cancel(); sl@0: iTurnaroundDfc.Cancel(); sl@0: } sl@0: return result; sl@0: } sl@0: sl@0: TInt DChannelComm::TurnaroundClear() sl@0: // Clear any old timer and start timer based on new turnaround timer sl@0: // Called for any change: from T > 0 to T == 0 or (T = t1 > 0) to (T = t2 > 0) sl@0: // POLICY: If a write has already been delayed, it will be started immediately if the requested sl@0: // turnaround time is elapsed else will only start after it is elapsed. sl@0: { sl@0: LOG(("TurnaroundClear()")); sl@0: TInt r = KErrNone; sl@0: TUint delta = 0; sl@0: sl@0: if(iTurnaroundTimerStartTimeValid == 1) sl@0: { sl@0: //Calculate the turnaround time elapsed so far. sl@0: delta = (NKern::TickCount() - iTurnaroundTimerStartTime) * NKern::TickPeriod(); sl@0: } sl@0: if(delta < iTurnaroundMicroSeconds) sl@0: { sl@0: iTurnaroundMinMilliSeconds = (iTurnaroundMicroSeconds - delta)/1000; sl@0: iTurnaroundTimerStartTimeValid = 3; //Just to make sure that the turnaround timer start time is not captured. sl@0: RestartTurnaroundTimer(); sl@0: } sl@0: else sl@0: { sl@0: if(TurnaroundStopTimer()) sl@0: { sl@0: // if a write is waiting, start a DFC to run it sl@0: TurnaroundStartDfcImplementation(EFalse); sl@0: } sl@0: } sl@0: iTurnaroundMinMilliSeconds = 0; sl@0: return r; sl@0: } sl@0: sl@0: TInt DChannelComm::RestartTurnaroundTimer() sl@0: { sl@0: LOG(("RestartTurnaroundTimer()")); sl@0: TInt r=KErrNone; sl@0: sl@0: // POLICY: if timer is running from a previous read, stop it and re-start it sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: TBool cancelDfcs = (iTurnaroundMinMilliSeconds > 0) && iTurnaroundTimerRunning; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: if (cancelDfcs) sl@0: { sl@0: iTurnaroundTimer.Cancel(); sl@0: iTurnaroundDfc.Cancel(); sl@0: } sl@0: sl@0: // Start the timer & update driver state to reflect that the timer is running sl@0: TInt timeout = 0; sl@0: irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: if(iTurnaroundMinMilliSeconds > 0) sl@0: { sl@0: iTurnaroundTimerRunning = ETrue; sl@0: timeout = NKern::TimerTicks(iTurnaroundMinMilliSeconds); sl@0: //Record the time stamp of turnaround timer start sl@0: if(iTurnaroundTimerStartTimeValid != 3) sl@0: iTurnaroundTimerStartTime = NKern::TickCount(); sl@0: iTurnaroundTimerStartTimeValid = 1; sl@0: } sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: if (timeout) sl@0: r=iTurnaroundTimer.OneShot(timeout); sl@0: return r; sl@0: } sl@0: sl@0: void DChannelComm::TurnaroundStartDfc(TAny* aSelf) sl@0: { sl@0: DChannelComm* self = (DChannelComm*)aSelf; sl@0: self->TurnaroundStartDfcImplementation(ETrue); // in ISR so Irqs are already disabled sl@0: } sl@0: sl@0: void DChannelComm::TurnaroundStartDfcImplementation(TBool aInIsr) sl@0: { sl@0: LOG(("TurnaroundStartDfcImplementation(inIsr=%d)", aInIsr)); sl@0: TInt irq=0; sl@0: if(!aInIsr) sl@0: irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: else sl@0: __SPIN_LOCK(iLock); sl@0: sl@0: iTurnaroundTimerRunning = EFalse; sl@0: if(iTurnaroundTransmitDelayed || iTurnaroundBreakDelayed) sl@0: { sl@0: if(aInIsr) sl@0: iTurnaroundDfc.Add(); sl@0: else sl@0: { sl@0: if(!aInIsr) sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: else sl@0: __SPIN_UNLOCK(iLock); sl@0: iTurnaroundDfc.Enque(); sl@0: return; sl@0: } sl@0: } sl@0: if(!aInIsr) sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: else sl@0: __SPIN_UNLOCK(iLock); sl@0: } sl@0: sl@0: void DChannelComm::TurnaroundTimeout(TAny* aSelf) sl@0: { sl@0: DChannelComm* self = (DChannelComm*)aSelf; sl@0: self->TurnaroundTimeoutImplementation(); sl@0: } sl@0: sl@0: void DChannelComm::TurnaroundTimeoutImplementation() sl@0: { sl@0: LOG(("TurnaroundTimeoutImplementation()")); sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: sl@0: if(iTurnaroundBreakDelayed) sl@0: { sl@0: iTurnaroundBreakDelayed=EFalse; sl@0: if (iStatus==EClosed) sl@0: { sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: Complete(EBreak, KErrNotReady); sl@0: return; sl@0: } sl@0: else if(IsLineFail(iFailSignals)) // have signals changed in the meantime? sl@0: { sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: Complete(EBreak, KErrCommsLineFail); // protected -> changed in signals ISR sl@0: return; sl@0: } sl@0: if (iTurnaroundTransmitDelayed) sl@0: { sl@0: //delay write by break instead of turnaround sl@0: iBreakDelayedTx = ETrue; sl@0: iTurnaroundTransmitDelayed=EFalse; sl@0: } sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: BreakOn(); sl@0: } sl@0: else if(iTurnaroundTransmitDelayed) sl@0: { sl@0: iTurnaroundTransmitDelayed = EFalse; // protected -> prevent reentrant ISR sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: sl@0: RestartDelayedTransmission(); sl@0: } sl@0: else sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: } sl@0: sl@0: void DChannelComm::ResetBuffers(TBool aResetTx) sl@0: // sl@0: // Reset the receive and maybe the transmit buffer. sl@0: // sl@0: { sl@0: LOG(("ResetBuffers(aResetTx=%d)", aResetTx)); sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: iRxPutIndex=0; sl@0: iRxGetIndex=0; sl@0: iRxBufCompleteIndex=0; sl@0: if (aResetTx) sl@0: { sl@0: iTxPutIndex=0; sl@0: iTxGetIndex=0; sl@0: } sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: sl@0: if (iStatus==EActive) sl@0: ReleaseFlowControl(); sl@0: iInputHeld=EFalse; sl@0: } sl@0: sl@0: TInt DChannelComm::TransmitIsr() sl@0: // sl@0: // Return the next character to be transmitted to the ISR sl@0: // sl@0: { sl@0: TInt tChar=iJamChar; // Look for control character to jam in sl@0: if (tChar>=0) // Control character to send sl@0: { sl@0: iJamChar=KTxNoChar; sl@0: } sl@0: else if (!iOutputHeld && iTxGetIndex!=iTxPutIndex) sl@0: { sl@0: // Get spinlock, disable interrupts to ensure we can reach the unlock sl@0: // statement. An FIQ before unlock that attempted to get lock would sl@0: // lead to CPU deadlock sl@0: TInt irqstate = __SPIN_LOCK_IRQSAVE(iLock); sl@0: sl@0: // output not held and buffer not empty, get next char sl@0: tChar=iTxBuffer[iTxGetIndex++]; sl@0: if (iTxGetIndex==iTxBufSize) sl@0: iTxGetIndex=0; sl@0: sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irqstate); sl@0: } sl@0: sl@0: return tChar; sl@0: } sl@0: sl@0: void DChannelComm::ReceiveIsr(TUint* aChar, TInt aCount, TInt aXonXoff) sl@0: // sl@0: // Handle received character block from the ISR. sl@0: // aChar points to received characters, aCount=number received, sl@0: // aXonXoff=1 if XON received, -1 if XOFF received, 0 if neither sl@0: // sl@0: { sl@0: if (aXonXoff>0) sl@0: { sl@0: iOutputHeld &= ~KXoffSignal; // Mark output ok. for XON/XOFF sl@0: if (iOutputHeld==0) sl@0: EnableTransmit(); sl@0: } sl@0: else if (aXonXoff<0) sl@0: { sl@0: iOutputHeld |= KXoffSignal; // Mark output held for XON/XOFF sl@0: } sl@0: if (aCount==0) // if only XON or XOFF received sl@0: return; sl@0: sl@0: // Get spinlock, disable interrupts to ensure we can reach the unlock sl@0: // statement. An FIQ before unlock that attempted to get lock would sl@0: // lead to CPU deadlock sl@0: TInt irqstate = __SPIN_LOCK_IRQSAVE(iLock); sl@0: sl@0: TInt count = RxCount(); sl@0: iReceived++; sl@0: sl@0: // At or above the high water mark send xoff every other character sl@0: if (count>=iFlowControlUpperThreshold && ((count&1)!=0 || aCount>1)) sl@0: AssertFlowControl(); sl@0: sl@0: TUint* pE=aChar+aCount; sl@0: TInt e=KErrNone; sl@0: TInt i=iRxPutIndex; sl@0: TInt g=iRxGetIndex; sl@0: TInt s=iRxBufSize; sl@0: g=g?g-1:s-1; sl@0: TInt p=iRxOutstanding?-1:0; sl@0: TInt thresh=iRxBufReq.iLen-iRxDesPos; sl@0: while(aChar>24); sl@0: sl@0: if (c & KReceiveIsrMaskError) sl@0: { sl@0: __UART_RX_ERROR(c); sl@0: if (c & KReceiveIsrOverrunError) sl@0: e = KErrCommsOverrun; sl@0: else if (c & KReceiveIsrBreakError) sl@0: e = KErrCommsBreak; sl@0: else if (c & KReceiveIsrFrameError) sl@0: e = KErrCommsFrame; sl@0: else if (c & KReceiveIsrParityError) sl@0: e = KErrCommsParity; sl@0: } sl@0: count++; sl@0: if (++i==s) sl@0: i=0; sl@0: if (p<0) sl@0: { sl@0: if (e || IsTerminator(TUint8(c)) || count==thresh) sl@0: { sl@0: // need to complete client request sl@0: iRxError = e; sl@0: p=i; sl@0: } sl@0: } sl@0: } sl@0: else sl@0: { sl@0: __OVERRUN(); sl@0: // buffer overrun, discard character sl@0: e=KErrCommsOverrun; sl@0: sl@0: // make sure client is informed of overrun error sl@0: iRxError=e; sl@0: sl@0: // discard remaining characters and complete sl@0: p=i; sl@0: break; sl@0: } sl@0: } sl@0: iRxPutIndex=i; sl@0: sl@0: if (iRxOutstanding) sl@0: { sl@0: if (p>=0) sl@0: { sl@0: // need to complete client request sl@0: iRxBufCompleteIndex=p; sl@0: iRxOutstanding=EFalse; sl@0: RxComplete(); sl@0: } sl@0: else if (count>=iRxDrainThreshold) sl@0: { sl@0: // drain buffer but don't complete sl@0: DrainRxBuffer(); sl@0: } sl@0: else if (iRxOneOrMore<0) sl@0: { sl@0: // doing read one or more - drain the buffer sl@0: // this will start the timer sl@0: iRxOneOrMore=1; sl@0: DrainRxBuffer(); sl@0: } sl@0: } sl@0: sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irqstate); sl@0: sl@0: if (iNotifyData) sl@0: { sl@0: iNotifyData=EFalse; sl@0: RxComplete(); sl@0: } sl@0: } sl@0: sl@0: void DChannelComm::CheckTxBuffer() sl@0: { sl@0: // if buffer count < threshold, fill from client buffer sl@0: TInt count=TxCount(); sl@0: if (iTxOutstanding && iTxDesPosIsReady() && ((iSignals^iNotifiedSignals)&iSigNotifyMask) ) sl@0: { sl@0: iSigNotifyDfc.Add(); sl@0: } sl@0: if (IsLineFail(iFailSignals)) sl@0: { sl@0: if (iRxOutstanding) sl@0: { sl@0: iRxError=KErrCommsLineFail; sl@0: iRxBufCompleteIndex=iRxPutIndex; sl@0: iRxOutstanding=EFalse; sl@0: RxComplete(); sl@0: } sl@0: if (iTxOutstanding) sl@0: { sl@0: iTxError = KErrCommsLineFail; sl@0: iTxOutstanding=EFalse; sl@0: TxComplete(); sl@0: } sl@0: } sl@0: sl@0: // sl@0: // Now we must determine if output is to be held sl@0: // sl@0: TUint status = ~iSignals & iHoldSignals; sl@0: if (iOutputHeld & KXoffSignal) sl@0: status |= KXoffSignal; // Leave the xon/xoff handshake bit sl@0: sl@0: LOG(("State - ISR - 0x%x",status)); sl@0: iOutputHeld=status; // record new flow control state sl@0: if (iTxGetIndex==iTxPutIndex) sl@0: { sl@0: // Tx buffer is empty sl@0: if (iTxOutstanding && iTxBufReq.iLen==0 && (status&~KXoffSignal)==0) sl@0: { sl@0: // if hardware flow control released, complete zero-length write sl@0: iTxOutstanding=EFalse; sl@0: TxComplete(); sl@0: } sl@0: } sl@0: else if (status==0) sl@0: { sl@0: // Tx buffer not empty and flow control released, so restart transmission sl@0: LOG(("Calling LDD:EnTx")); sl@0: EnableTransmit(); sl@0: } sl@0: } sl@0: sl@0: // check if transmitter is flow controlled sl@0: void DChannelComm::CheckOutputHeld() sl@0: { sl@0: iOutputHeld=(iOutputHeld & KXoffSignal) | (~iSignals & iHoldSignals); sl@0: LOG(("CheckOPH IOH = %d",iOutputHeld)); sl@0: } sl@0: sl@0: void DChannelComm::HandleMsg(TMessageBase* aMsg) sl@0: { sl@0: sl@0: if (iStandby) sl@0: { // postpone message handling to transition from standby sl@0: iMsgHeld=ETrue; sl@0: return; sl@0: } sl@0: sl@0: TThreadMessage& m=*(TThreadMessage*)aMsg; sl@0: LOG(("HandleMsg(%x a1=%x, a2=%x)", m.iValue, m.Int1(), m.Int2())); sl@0: TInt id=m.iValue; sl@0: if (id==(TInt)ECloseMsg) sl@0: { sl@0: Shutdown(); sl@0: iStatus = EClosed; sl@0: m.Complete(KErrNone, EFalse); sl@0: return; sl@0: } sl@0: else if (id==KMaxTInt) sl@0: { sl@0: // DoCancel sl@0: DoCancel(m.Int0()); sl@0: m.Complete(KErrNone,ETrue); sl@0: return; sl@0: } sl@0: sl@0: if (id<0) sl@0: { sl@0: // DoRequest sl@0: DoRequest(~id,m.Ptr1(),m.Ptr2()); sl@0: m.Complete(KErrNone,ETrue); sl@0: } sl@0: else sl@0: { sl@0: // DoControl sl@0: TInt r=DoControl(id,m.Ptr0(),m.Ptr1()); sl@0: m.Complete(r,ETrue); sl@0: } sl@0: } sl@0: sl@0: void DChannelComm::DoCancel(TInt aMask) sl@0: // sl@0: // Cancel an outstanding request. sl@0: // sl@0: { sl@0: LOG(("DoCancel(%d)", aMask)); sl@0: if (aMask & RBusDevComm::ERequestReadCancel) sl@0: { sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: iRxOutstanding=EFalse; sl@0: iNotifyData=EFalse; sl@0: iRxDesPos=0; sl@0: iRxBufReq.iLen=0; sl@0: iRxError=KErrNone; sl@0: iRxOneOrMore=0; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: iRxCompleteDfc.Cancel(); sl@0: iRxDrainDfc.Cancel(); sl@0: iTimer.Cancel(); sl@0: iTimerDfc.Cancel(); sl@0: Complete(ERx,KErrCancel); sl@0: } sl@0: if (aMask & RBusDevComm::ERequestWriteCancel) sl@0: { sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: iTurnaroundTransmitDelayed = EFalse; sl@0: iTxPutIndex=0; sl@0: iTxGetIndex=0; sl@0: iTxOutstanding=EFalse; sl@0: iTxDesPos=0; sl@0: iTxBufReq.iLen=0; sl@0: iTxError=KErrNone; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: iTxCompleteDfc.Cancel(); sl@0: iTxFillDfc.Cancel(); sl@0: Complete(ETx,KErrCancel); sl@0: } sl@0: if (aMask & RBusDevComm::ERequestNotifySignalChangeCancel) sl@0: { sl@0: iSigNotifyDfc.Cancel(); sl@0: Complete(ESigChg,KErrCancel); sl@0: } sl@0: if (aMask & RBusDevComm::ERequestBreakCancel) sl@0: { sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: if (iTurnaroundBreakDelayed) sl@0: iTurnaroundBreakDelayed=EFalse; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: sl@0: iBreakDfc.Cancel(); sl@0: iBreakTimer.Cancel(); sl@0: FinishBreakImplementation(KErrCancel); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Intercept messages in client context before they are sent to the DFC queue sl@0: */ sl@0: TInt DChannelComm::SendMsg(TMessageBase* aMsg) sl@0: { sl@0: TInt r = KErrNone; sl@0: TInt max; sl@0: TInt len = 0; sl@0: TThreadMessage* m = (TThreadMessage*)aMsg; sl@0: sl@0: // Handle ECloseMsg & Cancel sl@0: TInt id=aMsg->iValue; sl@0: if (id==(TInt)ECloseMsg || id==KMaxTInt) sl@0: { sl@0: LOG(("SendMsg(%s)", (id==KMaxTInt)?"Cancel":"ECloseMsg")); sl@0: // do nothing cos these are handled on the DFC side sl@0: } sl@0: sl@0: // Handle control messages that access user memory here in client context sl@0: else if (id >= 0) sl@0: { sl@0: TAny* a1 = m->iArg[0]; sl@0: switch (aMsg->iValue) sl@0: { sl@0: case RBusDevComm::EControlConfig: sl@0: { sl@0: LOG(("SendMsg(EControlConfig, %x)", a1)); sl@0: TPtrC8 cfg((const TUint8*)&iConfig,sizeof(iConfig)); sl@0: return Kern::ThreadDesWrite(iClient,a1,cfg,0,KTruncateToMaxLength,iClient); sl@0: } sl@0: case RBusDevComm::EControlSetConfig: sl@0: { sl@0: LOG(("SendMsg(EControlSetConfig, %x)", a1)); sl@0: if (AreAnyPending()) sl@0: ; // r = ESetConfigWhileRequestPending; sl@0: else sl@0: r = Kern::PinVirtualMemory(iPinObjSetConfig, (TLinAddr)a1, sizeof(TCommConfigV01)); sl@0: } sl@0: break; sl@0: case RBusDevComm::EControlCaps: sl@0: { sl@0: LOG(("SendMsg(EControlCaps, %x)", a1)); sl@0: TCommCaps2 caps; sl@0: PddCaps(caps); sl@0: return Kern::ThreadDesWrite(iClient,a1,caps,0,KTruncateToMaxLength,iClient); sl@0: } sl@0: default: sl@0: // Allow other control messages to go to DFC thread sl@0: LOG(("SendMsg(Ctrl %d, %x)", aMsg->iValue, a1)); sl@0: break; sl@0: } sl@0: } sl@0: sl@0: sl@0: // Handle requests sl@0: else sl@0: { sl@0: TRequestStatus* status = (TRequestStatus*)m->iArg[0]; sl@0: TAny* a1 = m->iArg[1]; sl@0: TAny* a2 = m->iArg[2]; sl@0: TInt reqNo = ~aMsg->iValue; sl@0: TInt irq; sl@0: switch (reqNo) sl@0: { sl@0: case RBusDevComm::ERequestRead: sl@0: { sl@0: iNotifyData=EFalse; sl@0: // If client has *not* provided a buffer pointer, it means they only want sl@0: // to know when data becomes available. sl@0: if (!a1) sl@0: { sl@0: irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: TBool isEmpty = (iRxPutIndex==iRxGetIndex); sl@0: iNotifyData = isEmpty; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: if (!isEmpty) // if Rx buffer has bytes in it we can complete the request immediately sl@0: { sl@0: Kern::RequestComplete(status, KErrNone); sl@0: return KErrNone; sl@0: } sl@0: // Do not start the Turnaround timer as this is not a Read request but a request for Data Available notification sl@0: LOG(("--Buf Empty--")); sl@0: } sl@0: sl@0: // Get buffer length if one has been given sl@0: if (a2) sl@0: r = Kern::ThreadRawRead(iClient,a2,&len,sizeof(len)); sl@0: sl@0: // Check the client descriptor is valid and large enough to hold the required amount of data. sl@0: if (a1 && r==KErrNone) sl@0: { sl@0: max = Kern::ThreadGetDesMaxLength(iClient, a1); sl@0: if (maxSetStatus(status); sl@0: LOG(("SendMsg(ERequestBreak, %x) bktime=%d r=%d", a1, iBreakTimeMicroSeconds, r)); sl@0: break; sl@0: sl@0: sl@0: // sl@0: // ERequestNotifySignalChange: a1 points to user-side int to receive the signals bitmask sl@0: // a2 points to the bitmask of signals the user is interested in sl@0: // sl@0: case RBusDevComm::ERequestNotifySignalChange: sl@0: LOG(("SendMsg(ERequestNotifySignalChange, %x, %x)", a1, a2)); sl@0: if (!a1 || !a2) sl@0: { sl@0: r = KErrArgument; sl@0: break; sl@0: } sl@0: // Setup word-sized client buffer sl@0: r = Kern::ThreadRawRead(iClient,a2,&iSigNotifyMask,sizeof(TUint)); sl@0: irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: if (r==KErrNone) sl@0: { sl@0: r = iSignalsReq->SetStatus(status); sl@0: if (r==KErrNone) sl@0: iSignalsReq->SetDestPtr(a1); sl@0: } sl@0: LOG(("ERequestNotifySignalChange: mask is %x, r is %d", iSigNotifyMask, r)); sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: break; sl@0: sl@0: sl@0: // Unknown request sl@0: default: sl@0: LOG(("SendMsg(req %d, %x, %x)", reqNo, a1, a2)); sl@0: r = KErrNotSupported; sl@0: break; sl@0: sl@0: } sl@0: sl@0: // If the request has an error, complete immediately sl@0: if (r!=KErrNone) sl@0: Kern::RequestComplete(status, r); sl@0: } sl@0: sl@0: // Send the client request to the DFC queue unless there's been an error sl@0: if (r==KErrNone) sl@0: r = DLogicalChannel::SendMsg(aMsg); sl@0: LOG(("DoSigNotify(); sl@0: } sl@0: sl@0: void DChannelComm::DoSigNotify() sl@0: { sl@0: // Atomically update iNotifiedSignals and prepare to signal sl@0: TBool do_notify = EFalse; sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: TUint orig_sig=iNotifiedSignals; sl@0: if (iSignalsReq->IsReady() && ( iNotifiedSignals==0xffffffff || ((iSignals^iNotifiedSignals)&iSigNotifyMask) ) ) sl@0: { sl@0: iNotifiedSignals=iSignals; sl@0: do_notify=ETrue; sl@0: } sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: __KTRACE_OPT(KHARDWARE,Kern::Printf("CommSig: Orig=%08x New %08x Mask %08x",orig_sig,iNotifiedSignals,iSigNotifyMask)); sl@0: if (do_notify) sl@0: { sl@0: TUint changed=iSigNotifyMask; sl@0: if (orig_sig!=0xffffffff) sl@0: changed&=(orig_sig^iNotifiedSignals); sl@0: changed=(changed<<12)|(iNotifiedSignals&iSigNotifyMask); sl@0: sl@0: // Write the result back to client memory and complete the request sl@0: __KTRACE_OPT(KHARDWARE,Kern::Printf("CommSig: Notify %08x",changed)); sl@0: LOG(("DoSigNotify: %08x",changed)); sl@0: TUint& rr = iSignalsReq->Data(); sl@0: rr = changed; sl@0: Kern::QueueRequestComplete(iClient, iSignalsReq, KErrNone); sl@0: } sl@0: } sl@0: sl@0: sl@0: /** sl@0: Manually read and act on signals sl@0: */ sl@0: void DChannelComm::UpdateAndProcessSignals() sl@0: { sl@0: TUint signals=Signals(); sl@0: TBool notify=EFalse; sl@0: TBool complete_rx=EFalse; sl@0: TBool complete_tx=EFalse; sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: iSignals=(iSignals&~KDTEInputSignals)|(signals&KDTEInputSignals); sl@0: if (iSignalsReq->IsReady() && ((iSignals^iNotifiedSignals)&iSigNotifyMask) ) sl@0: { sl@0: notify=ETrue; sl@0: } sl@0: if (IsLineFail(iFailSignals)) sl@0: { sl@0: if (iRxOutstanding) sl@0: { sl@0: iRxError=KErrCommsLineFail; sl@0: iRxBufCompleteIndex=iRxPutIndex; sl@0: iRxOutstanding=EFalse; sl@0: complete_rx=ETrue; sl@0: } sl@0: if (iTxOutstanding) sl@0: { sl@0: iTxError = KErrCommsLineFail; sl@0: iTxOutstanding=EFalse; sl@0: complete_tx=ETrue; sl@0: } sl@0: } sl@0: // sl@0: // Now we must determine if output is to be held sl@0: // sl@0: TUint status = ~iSignals & iHoldSignals; sl@0: if (iOutputHeld & KXoffSignal) sl@0: status |= KXoffSignal; // Leave the xon/xoff handshake bit sl@0: sl@0: iOutputHeld=status; // record new flow control state sl@0: if (iTxGetIndex==iTxPutIndex) sl@0: { sl@0: // Tx buffer is empty sl@0: if (iTxOutstanding && iTxBufReq.iLen==0 && (status&~KXoffSignal)==0) sl@0: { sl@0: // if hardware flow control released, complete zero-length write sl@0: iTxOutstanding=EFalse; sl@0: complete_tx=ETrue; sl@0: } sl@0: } sl@0: else if (status==0) sl@0: { sl@0: // Tx buffer not empty and flow control released, so restart transmission sl@0: EnableTransmit(); sl@0: } sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: if (notify) sl@0: DoSigNotify(); sl@0: if (complete_rx) sl@0: DoCompleteRx(); sl@0: if (complete_tx) sl@0: DoCompleteTx(); sl@0: } sl@0: sl@0: sl@0: TUint DChannelComm::FailSignals(TUint aHandshake) sl@0: { sl@0: TUint r=0; sl@0: if ((aHandshake&(KConfigObeyCTS|KConfigFailCTS))==(KConfigObeyCTS|KConfigFailCTS)) sl@0: r|=KSignalCTS; sl@0: if ((aHandshake&(KConfigObeyDSR|KConfigFailDSR))==(KConfigObeyDSR|KConfigFailDSR)) sl@0: r|=KSignalDSR; sl@0: if ((aHandshake&(KConfigObeyDCD|KConfigFailDCD))==(KConfigObeyDCD|KConfigFailDCD)) sl@0: r|=KSignalDCD; sl@0: return r; sl@0: } sl@0: sl@0: TUint DChannelComm::HoldSignals(TUint aHandshake) sl@0: { sl@0: TUint r=0; sl@0: if (aHandshake & KConfigObeyCTS) sl@0: r|=KSignalCTS; sl@0: if (aHandshake & KConfigObeyDSR) sl@0: r|=KSignalDSR; sl@0: if (aHandshake & KConfigObeyDCD) sl@0: r|=KSignalDCD; sl@0: return r; sl@0: } sl@0: sl@0: TUint DChannelComm::FlowControlSignals(TUint aHandshake) sl@0: { sl@0: TUint r=0; sl@0: if (!(aHandshake & KConfigFreeRTS)) sl@0: r|=KSignalRTS; sl@0: else if (!(aHandshake & KConfigFreeDTR)) sl@0: r|=KSignalDTR; sl@0: return r; sl@0: } sl@0: sl@0: TUint DChannelComm::AutoSignals(TUint aHandshake) sl@0: { sl@0: TUint r=0; sl@0: if (!(aHandshake & KConfigFreeRTS) && !(aHandshake & KConfigFreeDTR)) sl@0: r|=KSignalDTR; sl@0: return r; sl@0: } sl@0: sl@0: TInt DChannelComm::SetConfig(TCommConfigV01& c) sl@0: { sl@0: LOG(("SetConfig(...)")); sl@0: TBool restart = EFalse; sl@0: TBool purge = EFalse; sl@0: TBool changeTerminators=EFalse; sl@0: TInt irq; sl@0: TInt r; sl@0: sl@0: if(c.iTerminatorCount>KConfigMaxTerminators) sl@0: return KErrNotSupported; sl@0: if ((r=ValidateConfig(c))!=KErrNone) sl@0: return r; sl@0: TUint failSignals=FailSignals(c.iHandshake); sl@0: if (IsLineFail(failSignals)) sl@0: return KErrCommsLineFail; sl@0: if (iConfig.iRate != c.iRate sl@0: || iConfig.iDataBits != c.iDataBits sl@0: || iConfig.iStopBits != c.iStopBits sl@0: || iConfig.iParity != c.iParity sl@0: || iConfig.iFifo != c.iFifo sl@0: || iConfig.iSpecialRate != c.iSpecialRate sl@0: || iConfig.iSIREnable != c.iSIREnable sl@0: || iConfig.iSIRSettings != c.iSIRSettings) sl@0: { sl@0: restart = ETrue; sl@0: } sl@0: else if (iConfig.iParityErrorChar != c.iParityErrorChar sl@0: || iConfig.iParityError != c.iParityError sl@0: || iConfig.iXonChar != c.iXonChar sl@0: || iConfig.iXoffChar != c.iXoffChar sl@0: || (iConfig.iHandshake&(KConfigObeyXoff|KConfigSendXoff)) sl@0: != (c.iHandshake&(KConfigObeyXoff|KConfigSendXoff))) sl@0: { sl@0: purge = ETrue; sl@0: } sl@0: else sl@0: { sl@0: if (iConfig.iTerminatorCount==c.iTerminatorCount) sl@0: { sl@0: for (TInt i=0; iPanic(_L("D32COMM"),ESetReceiveBufferLength); sl@0: else sl@0: r=SetRxBufferSize((TInt)a1); sl@0: break; sl@0: // *************************************** sl@0: sl@0: case RBusDevComm::EControlMinTurnaroundTime: sl@0: r = iTurnaroundMicroSeconds; // used saved value sl@0: break; sl@0: sl@0: case RBusDevComm::EControlSetMinTurnaroundTime: sl@0: { sl@0: if (a1<0) sl@0: a1=(TAny*)0; sl@0: iTurnaroundMicroSeconds = (TUint)a1; // save this sl@0: TUint newTurnaroundMilliSeconds = (TUint)a1/1000; // convert to ms sl@0: if(newTurnaroundMilliSeconds != iTurnaroundMinMilliSeconds) sl@0: { sl@0: // POLICY: if a new turnaround time is set before the previous running timer has expired sl@0: // then the timer is adjusted depending on the new value and if any sl@0: // write request has been queued, transmission will proceed after the timer has expired. sl@0: if(iTurnaroundTimerStartTimeValid == 0) sl@0: { sl@0: iTurnaroundTimerStartTimeValid = 1; sl@0: iTurnaroundTimerStartTime = NKern::TickCount(); sl@0: } sl@0: if(iTurnaroundTimerStartTimeValid != 2) sl@0: TurnaroundClear(); sl@0: if(newTurnaroundMilliSeconds > 0) sl@0: { sl@0: r = TurnaroundSet(newTurnaroundMilliSeconds); sl@0: } sl@0: } sl@0: } sl@0: break; sl@0: default: sl@0: r=KErrNotSupported; sl@0: } sl@0: return(r); sl@0: } sl@0: sl@0: void DChannelComm::DoPowerUp() sl@0: // sl@0: // Called at switch on and upon Opening. sl@0: // sl@0: { sl@0: LOG(("DoPowerUp()")); sl@0: __KTRACE_OPT(KPOWER,Kern::Printf("DChannelComm::DoPowerUp()")); sl@0: sl@0: ResetBuffers(ETrue); sl@0: iRxOutstanding=EFalse; sl@0: iNotifyData=EFalse; sl@0: iTxOutstanding=EFalse; sl@0: iTxDesPos=0; sl@0: iFlags=0; sl@0: sl@0: // Cancel turnaround sl@0: iTurnaroundMinMilliSeconds = 0; sl@0: iTurnaroundTimerRunning = EFalse; sl@0: iTurnaroundTransmitDelayed = EFalse; sl@0: sl@0: // cancel any DFCs/timers sl@0: iRxDrainDfc.Cancel(); sl@0: iRxCompleteDfc.Cancel(); sl@0: iTxFillDfc.Cancel(); sl@0: iTxCompleteDfc.Cancel(); sl@0: iTimer.Cancel(); sl@0: iTurnaroundTimer.Cancel(); sl@0: iTurnaroundDfc.Cancel(); sl@0: iTimerDfc.Cancel(); sl@0: iSigNotifyDfc.Cancel(); sl@0: sl@0: Complete(EAll, KErrAbort); sl@0: if (!Kern::PowerGood()) sl@0: return; sl@0: TUint hand=iConfig.iHandshake; sl@0: if (hand&(KConfigFreeRTS|KConfigFreeDTR)) sl@0: { sl@0: Start(); sl@0: if (!Kern::PowerGood()) sl@0: return; sl@0: if (hand&KConfigFreeRTS) sl@0: { sl@0: if (iSignals&KSignalRTS) sl@0: SetSignals(KSignalRTS,0); sl@0: else sl@0: SetSignals(0,KSignalRTS); sl@0: } sl@0: if (!Kern::PowerGood()) sl@0: return; sl@0: if (hand&KConfigFreeDTR) sl@0: { sl@0: if (iSignals&KSignalDTR) sl@0: SetSignals(KSignalDTR,0); sl@0: else sl@0: SetSignals(0,KSignalDTR); sl@0: } sl@0: CheckOutputHeld(); sl@0: } sl@0: else sl@0: { sl@0: if (iStatus==EActive) sl@0: iStatus=EOpen; sl@0: } sl@0: } sl@0: sl@0: void DChannelComm::PowerUpDfc(TAny* aPtr) sl@0: { sl@0: sl@0: DChannelComm* d = (DChannelComm*)aPtr; sl@0: __PM_ASSERT(d->iStandby); sl@0: if (d->iStatus != EClosed) sl@0: d->DoPowerUp(); sl@0: else sl@0: // There is racing Close(): driver was already closed (ECloseMsg) but the DPowerHandler was not destroyed yet. sl@0: {} sl@0: d->iStandby = EFalse; sl@0: d->iPowerHandler->PowerUpDone(); sl@0: if (d->iMsgHeld) sl@0: { sl@0: __PM_ASSERT(d->iStatus != EClosed); sl@0: d->iMsgHeld = EFalse; sl@0: d->HandleMsg(d->iMsgQ.iMessage); sl@0: } sl@0: } sl@0: sl@0: void DChannelComm::PowerDownDfc(TAny* aPtr) sl@0: { sl@0: DChannelComm* d = (DChannelComm*)aPtr; sl@0: __PM_ASSERT(!d->iStandby); sl@0: d->iStandby = ETrue; sl@0: if (d->iStatus != EClosed) sl@0: d->Shutdown(); sl@0: else sl@0: // There is racing Close(): driver was already closed (ECloseMsg) but the DPowerHandler was not destroyed yet. sl@0: {} sl@0: d->iPowerHandler->PowerDownDone(); sl@0: } sl@0: sl@0: DCommPowerHandler::DCommPowerHandler(DChannelComm* aChannel) sl@0: : DPowerHandler(KLddName), sl@0: iChannel(aChannel) sl@0: { sl@0: } sl@0: sl@0: void DCommPowerHandler::PowerUp() sl@0: { sl@0: iChannel->iPowerUpDfc.Enque(); sl@0: } sl@0: sl@0: void DCommPowerHandler::PowerDown(TPowerState) sl@0: { sl@0: iChannel->iPowerDownDfc.Enque(); sl@0: } sl@0: sl@0: void DChannelComm::FinishBreak(TAny* aSelf) sl@0: { sl@0: DChannelComm* self = (DChannelComm*)aSelf; sl@0: self->QueueFinishBreakDfc(); sl@0: } sl@0: sl@0: void DChannelComm::QueueFinishBreakDfc() sl@0: { sl@0: iBreakDfc.Enque(); sl@0: } sl@0: sl@0: sl@0: void DChannelComm::FinishBreakDfc(TAny* aSelf) sl@0: { sl@0: DChannelComm* self = (DChannelComm*)aSelf; sl@0: self->FinishBreakImplementation(KErrNone); sl@0: } sl@0: sl@0: void DChannelComm::FinishBreakImplementation(TInt aError) sl@0: { sl@0: if (iStatus==EClosed) sl@0: { sl@0: Complete(EBreak, KErrNotReady); sl@0: } sl@0: else sl@0: { sl@0: BreakOff(); sl@0: Complete(EBreak, aError); sl@0: } sl@0: sl@0: // re-setup transmission if needed, for writes after a break sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: if (iBreakDelayedTx) sl@0: { sl@0: iBreakDelayedTx = EFalse; // protected -> prevent reentrant ISR sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: sl@0: RestartDelayedTransmission(); sl@0: } sl@0: else sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: } sl@0: void DChannelComm::RestartDelayedTransmission() sl@0: { sl@0: LOG(("RestartDelayedTransmission()")); sl@0: TInt irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: TBool completeTx=EFalse; sl@0: sl@0: iBreakDelayedTx = EFalse; // protected -> prevent reentrant ISR sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: sl@0: if (iStatus==EClosed) sl@0: { sl@0: irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: iTxError = KErrNotReady; // protected -> changed in signals ISR sl@0: completeTx = ETrue; sl@0: } sl@0: sl@0: else if(IsLineFail(iFailSignals)) // have signals changed in the meantime? sl@0: { sl@0: irq = __SPIN_LOCK_IRQSAVE(iLock); sl@0: iTxError = KErrCommsLineFail; // protected -> changed in signals ISR sl@0: completeTx = ETrue; sl@0: } sl@0: sl@0: else sl@0: { sl@0: InitiateWrite(); sl@0: } sl@0: sl@0: sl@0: if(completeTx) sl@0: { sl@0: iTxError = KErrNone; sl@0: __SPIN_UNLOCK_IRQRESTORE(iLock, irq); sl@0: Complete(ETx, iTxError); sl@0: } sl@0: }