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