navienginebsp/naviengine_assp/uart/uart16550_ne.cpp
changeset 0 5de814552237
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/navienginebsp/naviengine_assp/uart/uart16550_ne.cpp	Tue Sep 28 18:00:05 2010 +0100
@@ -0,0 +1,776 @@
+/*
+* Copyright (c) 2008-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:  
+* naviengine_assp\uart\uart16550.cpp
+* PDD for 16550 UART
+*
+*/
+
+
+
+#include <drivers/comm.h>
+#include <assp.h>
+#include "../naviengine_priv.h"
+#include "../naviengine.h"
+#include "uart16550_ne.h"
+#include <e32hal.h>
+
+_LIT(KPddName,"Comm.16550_ne");
+
+#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;
+
+// The following defines when enabled can help debugging uart:
+
+// RX_WORKAROUND - The basic workaround. There is a small window where data loss could occur
+// #define RX_WORKAROUND
+
+// RX_WORKAROUND_SAVE - A more advanced workaround that notifies the LDD that there
+// has been a read error. May cause new reporting of errors
+// #define RX_WORKAROUND_SAVE
+
+// TRACK_WORKAROUND - Records the number activations of the workaround
+// and the state of the last one. printf's the values on on deconstruct
+// #define TRACK_WORKAROUND
+
+// TRACK_DISTRIBUTION - Records the number of bytes read at a time to
+// demonstrate operation of fifo mode. printf's the values on on deconstruct
+// #define TRACK_DISTRIBUTION
+
+// DISABLE_RX_FLUSH - Disables RX Flush in uart configuration procedure.
+// #define DISABLE_RX_FLUSH
+
+// TEMPORARY_SMP_FIX reduces the number of bytes pumped into the Tx FIFO from 16 to 8
+// which works around a hardware problem not fully understood.
+#define TEMPORARY_SMP_FIX
+
+// configuration data
+static const TUint16 BaudRateDivisor[] =
+	{
+	KBaudRateDiv_50,
+	KBaudRateDiv_75,
+	KBaudRateDiv_110,
+	KBaudRateDiv_134,
+	KBaudRateDiv_150,
+	KBaudRateDiv_300,
+	KBaudRateDiv_600,
+	KBaudRateDiv_1200,
+	KBaudRateDiv_1800,
+	KBaudRateDiv_2000,
+	KBaudRateDiv_2400,
+	KBaudRateDiv_3600,
+	KBaudRateDiv_4800,
+	KBaudRateDiv_7200,
+	KBaudRateDiv_9600,
+	KBaudRateDiv_19200,
+	KBaudRateDiv_38400,
+	KBaudRateDiv_57600,
+	KBaudRateDiv_115200,
+	KBaudRateDiv_230400,
+	};
+
+class DDriverComm : public DPhysicalDevice
+	{
+public:
+	DDriverComm();
+	~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, TDynamicDfcQue* aDfcQ);
+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;
+	TDynamicDfcQue* iDfcQ;
+
+#ifdef TRACK_WORKAROUND
+	TInt iWorkaround;		 		 		 // how many times did the workaround fire
+	TInt iWorkaroundLSR;
+	TInt iWorkaroundISR;
+#endif
+
+#ifdef TRACK_DISTRIBUTION
+	TUint iRead[33];		 // array of how many bytes reported back to the ldd
+#endif
+	};
+
+
+DDriverComm::DDriverComm()
+//
+// Constructor
+//
+	{
+	iUnitsMask=~(0xffffffffu<<KNum16550Uarts);
+	iVersion=TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber);
+	}
+
+
+TInt DDriverComm::Install()
+//
+// Install the driver
+//
+	{
+	// Allocate a kernel thread to run the DFC
+	TInt r = Kern::DynamicDfcQCreate(iDfcQ, KUart16550ThreadPriority, KUar16550tDriverThread);
+	if (r == KErrNone)
+		{
+#ifdef CPU_AFFINITY_ANY
+	NKern::ThreadSetCpuAffinity((NThread*)(iDfcQ->iThread), KCpuAffinityAny);
+#endif
+		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 =
+		 KCapsBps150
+		|KCapsBps300
+		|KCapsBps600
+		|KCapsBps1200
+		|KCapsBps1800
+		|KCapsBps2000
+		|KCapsBps2400
+		|KCapsBps3600
+		|KCapsBps4800
+		|KCapsBps7200
+		|KCapsBps9600
+		|KCapsBps19200
+		|KCapsBps38400
+		|KCapsBps57600
+		|KCapsBps115200;
+	if (aUnit != 2)
+		{
+		c.iRate |= KCapsBps230400;
+		}
+
+	c.iDataBits =
+		 KCapsData5
+		|KCapsData6
+		|KCapsData7
+		|KCapsData8;
+	c.iStopBits =
+		 KCapsStop1
+		|KCapsStop2;
+	c.iParity =
+		 KCapsParityNone
+		|KCapsParityEven
+		|KCapsParityOdd
+		|KCapsParityMark
+		|KCapsParitySpace;
+	c.iSIR = 0;
+	switch (aUnit)
+		{
+	case 0:
+		c.iHandshake =
+			 KCapsObeyXoffSupported
+			|KCapsSendXoffSupported
+			|KCapsObeyCTSSupported
+			|KCapsFailCTSSupported
+			|KCapsFreeRTSSupported;
+		c.iSignals =
+			KCapsSignalCTSSupported
+			|KCapsSignalRTSSupported;
+		break;
+	case 1:
+		c.iHandshake =
+			 KCapsObeyXoffSupported
+			|KCapsSendXoffSupported
+			|KCapsObeyCTSSupported
+			|KCapsFailCTSSupported
+			|KCapsObeyDSRSupported
+			|KCapsFailDSRSupported
+			|KCapsObeyDCDSupported
+			|KCapsFailDCDSupported
+			|KCapsFreeRTSSupported
+			|KCapsFreeDTRSupported;
+		c.iSignals =
+			 KCapsSignalCTSSupported
+			|KCapsSignalRTSSupported
+			|KCapsSignalDTRSupported
+			|KCapsSignalDSRSupported
+			|KCapsSignalDCDSupported;
+		break;
+	default:
+	case 2:
+		c.iHandshake =
+			 KCapsObeyXoffSupported
+			|KCapsSendXoffSupported;
+		c.iSignals = 0;
+		break;
+		}
+	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, iDfcQ);
+	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 (TUint(aUnit)>=TUint(KNum16550Uarts))
+		return KErrNotSupported;
+	return KErrNone;
+	}
+
+DComm16550::DComm16550()
+//
+// Constructor
+//
+	{
+//		 iTransmitting=EFalse;
+	iInterruptId=-1;		 		 // -1 means not bound
+	}
+
+DComm16550::~DComm16550()
+//
+// Destructor
+//
+	{
+	if (iUart)
+		delete iUart;
+	if (iInterruptId>=0)
+		Interrupt::Unbind(iInterruptId);
+
+#ifdef TRACK_WORKAROUND
+	if (iWorkaround)
+		{
+		Kern::Printf("########################################");
+		Kern::Printf("####%d iWorkaround %d LSR 0x%x ISR 0x%x", iUnit, iWorkaround, iWorkaroundLSR, iWorkaroundISR);
+		Kern::Printf("########################################");
+		}
+	else
+		{
+		Kern::Printf("####%d iWorkaround %d LSR 0x%x ISR 0x%x", iUnit, iWorkaround, iWorkaroundLSR, iWorkaroundISR);
+		}
+#endif
+#ifdef TRACK_DISTRIBUTION
+	Kern::Printf("####%d iRead: %d, %d %d %d %d %d %d %d %d, %d %d %d %d %d %d %d %d, %d %d %d %d %d %d %d %d, %d %d %d %d %d %d %d",
+	             iUnit,
+	             iRead[0],
+	             iRead[1], iRead[2], iRead[3], iRead[4], iRead[5], iRead[6], iRead[7], iRead[8],
+	             iRead[9], iRead[10], iRead[11], iRead[12], iRead[13], iRead[14], iRead[15], iRead[16],
+	             iRead[17], iRead[18], iRead[19], iRead[20], iRead[21], iRead[22], iRead[23], iRead[24],
+	             iRead[25], iRead[26], iRead[27], iRead[28], iRead[29], iRead[30], iRead[31], iRead[32]);
+#endif
+	}
+
+TInt DComm16550::DoCreate(TInt aUnit, const TDesC8* /*anInfo*/,  TDynamicDfcQue* aDfcQ)
+//
+// Sets up the PDD
+//
+	{
+	TUint base;
+
+	switch (aUnit)
+		{
+	case 0:
+		base=KHwBaseUart0;
+		break;
+	case 1:
+		base=KHwBaseUart1;
+		break;
+	case 2:
+		base=KHwBaseUart2;
+		break;
+	default:
+		return KErrNotSupported;
+		}
+
+	iUnit=aUnit;
+	iDfcQ = aDfcQ;
+	TInt irq=KIntIdUart0+aUnit;
+
+	// bind to UART interrupt
+	TInt r=Interrupt::Bind(irq,Isr,this);
+	if (r>=0)
+		{
+		iInterruptId = r;
+		r = KErrNone;
+
+		iUart = new T16550Uart;
+		if (iUart)
+			{
+			iUart->iBase = (TUint8*)base;
+			iUart->SetIER(0);
+			iUart->SetLCR(0);
+			iUart->SetFCR(0);
+			iUart->SetMCR(0);
+			}
+		else
+			{
+			Interrupt::Unbind(iInterruptId);
+			r = KErrNoMemory;
+			}
+		}
+
+#ifdef TRACK_WORKAROUND
+	iWorkaround = iWorkaroundLSR = iWorkaroundISR = 0;
+#endif
+	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;
+		}
+
+	/*
+	S18599EJ1V5UM.pdf page 746 uart configuration
+	   Caution: Must not change the baud rate while any data transfers are executing.
+	   If the baud rate is necessary to changed, please apply following sequence.
+	   ... (inlined with code) ...
+	 */
+
+	// 1) Put UART in Local loop-back mode (write 1 to MCR4 bit).
+	iUart->ModifyMCR(0,K16550MCR_LocalLoop);
+
+	// 2) Clear transmit FIFO (write 1 to FCR2 bit).
+	iUart->SetFCR(K16550FCR_TxReset);
+
+	// 3) Wait 1 character period (ex. 100µs at 115kbps)
+	NKern::Sleep(1);		 // sleep 1ms (for, 9600 bump up to 2ms?)
+
+	// 4) Clear receive buffer (read RBR register until LSR0 bit becomes 0).
+	TInt maxdrain = 1000; // arbitrary count (fifo depth is 16 bytes)
+	while (iUart->TestLSR(K16550LSR_RxReady) && --maxdrain)
+		iUart->RxData(); // read character from RBR
+
+	// 5) Clear receive FIFO (write 1 to FCR1 bit).
+	iUart->SetFCR(K16550FCR_RxReset);
+
+	// 6) Change the baud rate (write 1 to LCR7, write DLL and DLH, and then write 0 to LCR7).
+	iUart->SetLCR(lcr|K16550LCR_DLAB);
+	iUart->SetBaudRateDivisor(BaudRateDivisor[(TInt)aConfig.iRate]);
+	iUart->SetLCR(lcr);
+
+	// 7) Go back to normal mode (write 0 to MCR4 bit).
+	iUart->ModifyMCR(K16550MCR_LocalLoop, 0);
+
+	// Enable fifo mode
+	//iUart->SetFCR(K16550FCR_Enable | K16550FCR_TxRxRdy | K16550FCR_RxTrig8);
+	iUart->SetFCR(K16550FCR_Enable | K16550FCR_RxTrig8);
+
+#ifndef	 DISABLE_RX_FLUSH
+	// Read characters from RBR; this isn't part of the recommended baud rate change code
+	// but this has fixed the problem with ISR running indefinitely.
+	TInt j;
+	for (j=0;j<16;j++)	 // Fifo buffer is 16 bytes deep
+		iUart->RxData();
+#endif
+
+#ifdef TRACK_DISTRIBUTION
+	// blank an array of 33 integers 0 to 32
+	TInt i=0;
+	for (i=0;i<33;i++)
+		{
+		iRead[i]=0;
+		}
+#endif
+	}
+
+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;
+
+#if defined(RX_WORKAROUND) || defined(RX_WORKAROUND_SAVE)
+		// It has been observed that the hardware can assert RDAI without asserting RxReady
+		// This is nasty but it reads from the head of the fifo, ORs in a faked error
+		// and saves it in the buffer, later on in the loop it will be passed to the ldd
+		// as data with an error
+		if (!(u.LSR() & K16550LSR_RxReady))
+			{
+#ifdef TRACK_WORKAROUND
+			++d.iWorkaround;
+			d.iWorkaroundLSR=u.LSR();
+			d.iWorkaroundISR=u.ISR();
+#endif
+
+#ifdef RX_WORKAROUND_SAVE
+			// save the character and flag error, this is a bit risky as it could report errors
+			rx[rxi++]=(u.RxData() | KReceiveIsrFrameError);
+#else // or just discard it (normal workaround)
+			u.RxData();
+#endif
+			}
+#endif
+		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;
+			}
+#ifdef TRACK_DISTRIBUTION
+		d.iRead[rxi]++;		 		 // record how many bytes we're reporting
+#endif
+		d.ReceiveIsr(rx,rxi,x);
+		return;
+		}
+	// if TFS flag and TIE
+	if (isr==K16550ISR_THREI)
+		{
+		TInt n;
+#ifdef TEMPORARY_SMP_FIX
+		for (n=0; n<8; ++n)
+#else
+		for (n=0; n<16; ++n)
+#endif
+			{
+			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());
+	}
+
+DECLARE_STANDARD_PDD()
+	{
+	return new DDriverComm;
+	}
+