sl@0: // Copyright (c) 1998-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\uart16550.cpp sl@0: // PDD for 16550 UART sl@0: // sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: _LIT(KPddName,"Comm.16550"); sl@0: sl@0: #define __COMMS_MACHINE_CODED__ sl@0: #ifdef __COMMS_MACHINE_CODED__ sl@0: #define DBASE_VPTR_OFFSET 4 sl@0: #define RX_ISR_VT_OFFSET 0x24 sl@0: #define CHK_TXB_VT_OFFSET 0x28 sl@0: #define STATE_ISR_VT_OFFSET 0x2C sl@0: #endif sl@0: sl@0: // needs ldd version.. sl@0: const TInt KMinimumLddMajorVersion=1; sl@0: const TInt KMinimumLddMinorVersion=1; sl@0: const TInt KMinimumLddBuild=122; sl@0: sl@0: // configuration data sl@0: static const TUint16 BaudRateDivisor[19] = sl@0: { sl@0: 2304, 1536, 1047, 860, 768, 384, 192, 96, sl@0: 64, 58, 48, 32, 24, 16, 12, 6, sl@0: 3, 2, 1 sl@0: }; sl@0: sl@0: class DDriverComm : public DPhysicalDevice sl@0: { sl@0: public: sl@0: DDriverComm(); sl@0: virtual TInt Install(); sl@0: virtual void GetCaps(TDes8 &aDes) const; sl@0: virtual TInt Create(DBase*& aChannel, TInt aUnit, const TDesC8* anInfo, const TVersion &aVer); sl@0: virtual TInt Validate(TInt aUnit, const TDesC8* anInfo, const TVersion &aVer); sl@0: public: sl@0: TDynamicDfcQue* iDfcQ; sl@0: }; sl@0: sl@0: class DComm16550 : public DComm sl@0: { sl@0: public: sl@0: DComm16550(); sl@0: ~DComm16550(); sl@0: TInt DoCreate(TInt aUnit, const TDesC8* anInfo); sl@0: public: sl@0: virtual TInt Start(); sl@0: virtual void Stop(TStopMode aMode); sl@0: virtual void Break(TBool aState); sl@0: virtual void EnableTransmit(); sl@0: virtual TUint Signals() const; sl@0: virtual void SetSignals(TUint aSetMask,TUint aClearMask); sl@0: virtual TInt ValidateConfig(const TCommConfigV01 &aConfig) const; sl@0: virtual void Configure(TCommConfigV01 &aConfig); sl@0: virtual void Caps(TDes8 &aCaps) const; sl@0: virtual TInt DisableIrqs(); sl@0: virtual void RestoreIrqs(TInt aIrq); sl@0: virtual TDfcQue* DfcQ(TInt aUnit); sl@0: virtual void CheckConfig(TCommConfigV01& aConfig); sl@0: public: sl@0: static void Isr(TAny* aPtr); sl@0: public: sl@0: TInt iInterruptId; sl@0: TInt iUnit; sl@0: T16550Uart* iUart; sl@0: }; sl@0: sl@0: sl@0: DDriverComm::DDriverComm() sl@0: // sl@0: // Constructor sl@0: // sl@0: { sl@0: iUnitsMask=~(0xffffffffu<SetRealtimeState(ERealtimeStateOff); sl@0: r = SetName(&KPddName); sl@0: } sl@0: return r; sl@0: } sl@0: sl@0: /** sl@0: Destructor sl@0: */ sl@0: DDriverComm::~DDriverComm() sl@0: { sl@0: if (iDfcQ) sl@0: iDfcQ->Destroy(); sl@0: } sl@0: sl@0: void Get16550CommsCaps(TDes8& aCaps, TInt aUnit) sl@0: { sl@0: TCommCaps3 capsBuf; sl@0: TCommCapsV03 &c=capsBuf(); sl@0: c.iRate=KCapsBps110|KCapsBps150|KCapsBps300|KCapsBps600\ sl@0: |KCapsBps1200|KCapsBps2400|KCapsBps4800|KCapsBps9600\ sl@0: |KCapsBps19200|KCapsBps38400|KCapsBps57600|KCapsBps115200; sl@0: c.iDataBits=KCapsData5|KCapsData6|KCapsData7|KCapsData8; sl@0: c.iStopBits=KCapsStop1|KCapsStop2; sl@0: c.iParity=KCapsParityNone|KCapsParityEven|KCapsParityOdd|KCapsParityMark|KCapsParitySpace; sl@0: c.iHandshake=KCapsObeyXoffSupported|KCapsSendXoffSupported| sl@0: KCapsObeyCTSSupported|KCapsFailCTSSupported| sl@0: KCapsObeyDSRSupported|KCapsFailDSRSupported| sl@0: KCapsObeyDCDSupported|KCapsFailDCDSupported| sl@0: KCapsFreeRTSSupported|KCapsFreeDTRSupported; sl@0: c.iSIR=0; sl@0: c.iSignals=KCapsSignalCTSSupported|KCapsSignalRTSSupported|KCapsSignalDTRSupported| sl@0: KCapsSignalDSRSupported|KCapsSignalDCDSupported|KCapsSignalRNGSupported; sl@0: c.iFifo=KCapsHasFifo; sl@0: c.iNotificationCaps=KNotifyDataAvailableSupported|KNotifySignalsChangeSupported; sl@0: c.iRoleCaps=0; sl@0: c.iFlowControlCaps=0; sl@0: c.iBreakSupported=ETrue; sl@0: aCaps.FillZ(aCaps.MaxLength()); sl@0: aCaps=capsBuf.Left(Min(capsBuf.Length(),aCaps.MaxLength())); sl@0: } sl@0: sl@0: void DDriverComm::GetCaps(TDes8 &aDes) const sl@0: // sl@0: // Return the drivers capabilities sl@0: // sl@0: { sl@0: Get16550CommsCaps(aDes, 0); sl@0: } sl@0: sl@0: TInt DDriverComm::Create(DBase*& aChannel, TInt aUnit, const TDesC8* anInfo, const TVersion& aVer) sl@0: // sl@0: // Create a driver sl@0: // sl@0: { sl@0: DComm16550* pD=new DComm16550; sl@0: aChannel=pD; sl@0: TInt r=KErrNoMemory; sl@0: if (pD) sl@0: r=pD->DoCreate(aUnit,anInfo); sl@0: return r; sl@0: } sl@0: sl@0: TInt DDriverComm::Validate(TInt aUnit, const TDesC8* /*anInfo*/, const TVersion& aVer) sl@0: // sl@0: // Validate the requested configuration sl@0: // sl@0: { sl@0: if ((!Kern::QueryVersionSupported(iVersion,aVer)) || (!Kern::QueryVersionSupported(aVer,TVersion(KMinimumLddMajorVersion,KMinimumLddMinorVersion,KMinimumLddBuild)))) sl@0: return KErrNotSupported; sl@0: if (aUnit<0 || aUnit>=KNum16550Uarts) sl@0: return KErrNotSupported; sl@0: return KErrNone; sl@0: } sl@0: sl@0: DComm16550::DComm16550() sl@0: // sl@0: // Constructor sl@0: // sl@0: { sl@0: // iTransmitting=EFalse; sl@0: iInterruptId=-1; // -1 means not bound sl@0: } sl@0: sl@0: DComm16550::~DComm16550() sl@0: // sl@0: // Destructor sl@0: // sl@0: { sl@0: if (iInterruptId>=0) sl@0: Interrupt::Unbind(iInterruptId); sl@0: } sl@0: sl@0: TInt DComm16550::DoCreate(TInt aUnit, const TDesC8* /*anInfo*/) sl@0: // sl@0: // Sets up the PDD sl@0: // sl@0: { sl@0: iUnit=aUnit; sl@0: TInt irq=IrqFromUnit(aUnit); sl@0: sl@0: // bind to UART interrupt sl@0: TInt r=Interrupt::Bind(irq,Isr,this); sl@0: if (r==KErrNone) sl@0: { sl@0: iInterruptId=irq; sl@0: iUart=UartFromUnit(aUnit); sl@0: iUart->SetIER(0); sl@0: iUart->SetLCR(0); sl@0: iUart->SetFCR(0); sl@0: iUart->SetMCR(0); sl@0: } sl@0: return r; sl@0: } sl@0: sl@0: TDfcQue* DComm16550::DfcQ(TInt /*aUnit*/) sl@0: // sl@0: // Return the DFC queue to be used for this device sl@0: // For PC cards, use the PC card controller thread for the socket in question. sl@0: // sl@0: sl@0: { sl@0: return iDfcQ; sl@0: } sl@0: sl@0: TInt DComm16550::Start() sl@0: // sl@0: // Start receiving characters sl@0: // sl@0: { sl@0: // if EnableTransmit() called before Start() sl@0: iTransmitting=EFalse; sl@0: iUart->SetIER(K16550IER_RDAI|K16550IER_RLSI|K16550IER_MSI); sl@0: iLdd->UpdateSignals(Signals()); sl@0: Interrupt::Enable(iInterruptId); sl@0: return KErrNone; sl@0: } sl@0: sl@0: TBool FinishedTransmitting(TAny* aPtr) sl@0: { sl@0: DComm16550& d=*(DComm16550*)aPtr; sl@0: return d.iUart->TestLSR(K16550LSR_TxIdle); sl@0: } sl@0: sl@0: void DComm16550::Stop(TStopMode aMode) sl@0: // sl@0: // Stop receiving characters sl@0: // sl@0: { sl@0: switch (aMode) sl@0: { sl@0: case EStopNormal: sl@0: case EStopPwrDown: sl@0: iUart->SetIER(0); sl@0: Interrupt::Disable(iInterruptId); sl@0: iTransmitting=EFalse; sl@0: sl@0: // wait for uart to stop tranmitting sl@0: Kern::PollingWait(FinishedTransmitting,this,3,100); sl@0: break; sl@0: case EStopEmergency: sl@0: iUart->SetIER(0); sl@0: Interrupt::Disable(iInterruptId); sl@0: iTransmitting=EFalse; sl@0: break; sl@0: } sl@0: } sl@0: sl@0: void DComm16550::Break(TBool aState) sl@0: // sl@0: // Start or stop the uart breaking sl@0: // sl@0: { sl@0: if (aState) sl@0: iUart->ModifyLCR(0,K16550LCR_TxBreak); sl@0: else sl@0: iUart->ModifyLCR(K16550LCR_TxBreak,0); sl@0: } sl@0: sl@0: void DComm16550::EnableTransmit() sl@0: // sl@0: // Start sending characters. sl@0: // sl@0: { sl@0: TBool tx = (TBool)__e32_atomic_swp_ord32(&iTransmitting, 1); sl@0: if (tx) sl@0: return; sl@0: iUart->ModifyIER(0,K16550IER_THREI); sl@0: } sl@0: sl@0: TUint DComm16550::Signals() const sl@0: // sl@0: // Read and translate the modem lines sl@0: // sl@0: { sl@0: TUint msr=iUart->MSR(); sl@0: msr=((msr>>4)&0x0f); // true input signals sl@0: TUint sig=msr & 3; // CTS,DSR OK sl@0: if (msr & 4) sl@0: sig|=KSignalRNG; // swap DCD,RNG sl@0: if (msr & 8) sl@0: sig|=KSignalDCD; sl@0: return sig; sl@0: } sl@0: sl@0: void DComm16550::SetSignals(TUint aSetMask, TUint aClearMask) sl@0: // sl@0: // Set signals. sl@0: // sl@0: { sl@0: TUint set=0; sl@0: TUint clear=0; sl@0: if (aSetMask & KSignalRTS) sl@0: set|=K16550MCR_RTS; sl@0: if (aSetMask & KSignalDTR) sl@0: set|=K16550MCR_DTR; sl@0: if (aClearMask & KSignalRTS) sl@0: clear|=K16550MCR_RTS; sl@0: if (aClearMask & KSignalDTR) sl@0: clear|=K16550MCR_DTR; sl@0: iUart->ModifyMCR(clear,set); sl@0: } sl@0: sl@0: TInt DComm16550::ValidateConfig(const TCommConfigV01 &aConfig) const sl@0: // sl@0: // Check a config structure. sl@0: // sl@0: { sl@0: if (aConfig.iSIREnable==ESIREnable) sl@0: return KErrNotSupported; sl@0: switch (aConfig.iParity) sl@0: { sl@0: case EParityNone: sl@0: case EParityOdd: sl@0: case EParityEven: sl@0: case EParityMark: sl@0: case EParitySpace: sl@0: break; sl@0: default: sl@0: return KErrNotSupported; sl@0: } sl@0: if (TUint(aConfig.iRate)>TUint(EBps115200)) sl@0: return KErrNotSupported; sl@0: return KErrNone; sl@0: } sl@0: sl@0: void DComm16550::CheckConfig(TCommConfigV01& aConfig) sl@0: { sl@0: // do nothing sl@0: } sl@0: sl@0: TInt DComm16550::DisableIrqs() sl@0: // sl@0: // Disable normal interrupts sl@0: // sl@0: { sl@0: sl@0: return NKern::DisableInterrupts(1); sl@0: } sl@0: sl@0: void DComm16550::RestoreIrqs(TInt aLevel) sl@0: // sl@0: // Restore normal interrupts sl@0: // sl@0: { sl@0: sl@0: NKern::RestoreInterrupts(aLevel); sl@0: } sl@0: sl@0: void DComm16550::Configure(TCommConfigV01 &aConfig) sl@0: // sl@0: // Set up the Uart sl@0: // sl@0: { sl@0: // wait for uart to stop tranmitting sl@0: Kern::PollingWait(FinishedTransmitting,this,3,100); sl@0: sl@0: TUint lcr=0; sl@0: switch (aConfig.iDataBits) sl@0: { sl@0: case EData5: lcr=K16550LCR_Data5; break; sl@0: case EData6: lcr=K16550LCR_Data6; break; sl@0: case EData7: lcr=K16550LCR_Data7; break; sl@0: case EData8: lcr=K16550LCR_Data8; break; sl@0: } sl@0: switch (aConfig.iStopBits) sl@0: { sl@0: case EStop1: break; sl@0: case EStop2: lcr|=K16550LCR_Stop2; break; sl@0: } sl@0: switch (aConfig.iParity) sl@0: { sl@0: case EParityNone: break; sl@0: case EParityEven: lcr|=K16550LCR_ParityEnable|K16550LCR_ParityEven; break; sl@0: case EParityOdd: lcr|=K16550LCR_ParityEnable; break; sl@0: case EParityMark: lcr|=K16550LCR_ParityEnable|K16550LCR_ParityMark; break; sl@0: case EParitySpace: lcr|=K16550LCR_ParityEnable|K16550LCR_ParitySpace; break; sl@0: } sl@0: iUart->SetLCR(lcr|K16550LCR_DLAB); sl@0: iUart->SetBaudRateDivisor(BaudRateDivisor[(TInt)aConfig.iRate]); sl@0: iUart->SetLCR(lcr); sl@0: iUart->SetFCR(K16550FCR_Enable|K16550FCR_RxReset|K16550FCR_TxReset|K16550FCR_RxTrig8); sl@0: } sl@0: sl@0: void DComm16550::Caps(TDes8 &aCaps) const sl@0: // sl@0: // return our caps sl@0: // sl@0: { sl@0: Get16550CommsCaps(aCaps,iUnit); sl@0: } sl@0: sl@0: void DComm16550::Isr(TAny* aPtr) sl@0: // sl@0: // Service the UART interrupt sl@0: // sl@0: { sl@0: DComm16550& d=*(DComm16550*)aPtr; sl@0: T16550Uart& u=*d.iUart; sl@0: TUint rx[32]; sl@0: TUint xon=d.iLdd->iRxXonChar; sl@0: TUint xoff=d.iLdd->iRxXoffChar; sl@0: sl@0: TUint isr=u.ISR(); sl@0: if (isr & K16550ISR_NotPending) sl@0: return; sl@0: isr&=K16550ISR_IntIdMask; sl@0: sl@0: // if receive data available or line status interrupt sl@0: if (isr==K16550ISR_RDAI || isr==K16550ISR_RLSI) sl@0: { sl@0: TInt rxi=0; sl@0: TInt x=0; sl@0: while(u.TestLSR(K16550LSR_RxReady|K16550LSR_RxParityErr|K16550LSR_RxOverrun|K16550LSR_RxFrameErr|K16550LSR_RxBreak) && Kern::PowerGood()) sl@0: { sl@0: TUint lsr=0; sl@0: // checks for EIF flag sl@0: if (isr==K16550ISR_RLSI) sl@0: lsr=u.LSR()&(K16550LSR_RxParityErr|K16550LSR_RxOverrun|K16550LSR_RxFrameErr); sl@0: TUint ch=u.RxData(); sl@0: // if error in this character sl@0: if(lsr) sl@0: { sl@0: if (lsr & K16550LSR_RxParityErr) sl@0: ch|=KReceiveIsrParityError; sl@0: if (lsr & K16550LSR_RxBreak) sl@0: ch|=KReceiveIsrBreakError; sl@0: if (lsr & K16550LSR_RxFrameErr) sl@0: ch|=KReceiveIsrFrameError; sl@0: if (lsr & K16550LSR_RxOverrun) sl@0: ch|=KReceiveIsrOverrunError; sl@0: } sl@0: if (ch==xon) sl@0: x=1; sl@0: else if (ch==xoff) sl@0: x=-1; sl@0: else sl@0: rx[rxi++]=ch; sl@0: } sl@0: d.ReceiveIsr(rx,rxi,x); sl@0: return; sl@0: } sl@0: // if TFS flag and TIE sl@0: if (isr==K16550ISR_THREI) sl@0: { sl@0: TInt n; sl@0: for (n=0; n<16; ++n) sl@0: { sl@0: TInt r=d.TransmitIsr(); sl@0: if(r<0) sl@0: { sl@0: //no more to send sl@0: // Disable the TX interrupt sl@0: u.ModifyIER(K16550IER_THREI,0); sl@0: d.iTransmitting=EFalse; sl@0: break; sl@0: } sl@0: u.SetTxData(r); sl@0: } sl@0: d.CheckTxBuffer(); sl@0: return; sl@0: } sl@0: // must be signal change sl@0: d.StateIsr(d.Signals()); sl@0: } sl@0: sl@0: sl@0: const TInt KUart16550ThreadPriority = 27; sl@0: _LIT(KUar16550tDriverThread,"UART16550_Thread"); sl@0: sl@0: DECLARE_STANDARD_PDD() sl@0: { sl@0: return new DDriverComm; sl@0: } sl@0: