// 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:
// template\template_variant\specific\uart.cpp
// pdd for serial ports
// assume Modem Control Signals change cause an interrupt
//
//
#include <drivers/comm.h>
#include <template_assp.h>
#include "iolines.h"
#include <e32hal.h>
_LIT(KPddName,"Comm.Template");
// needs ldd version..
const TInt KMinimumLddMajorVersion=1;
const TInt KMinimumLddMinorVersion=1;
const TInt KMinimumLddBuild=122;
//
// TO DO: (mandatory)
//
// Define here the UART enumeration data for the serial ports.
// It is a good idea to define each enumerated value as a bit mask that can be written to (or OR-ed or AND-ed to)
// a hardware register to provide the configuration option desired (the following are EXAMPLES ONLY):
//
// EXAMPLE ONLY
enum TUartBaudRate
{
EUartBaudRate115200/* =bitmask for 115200 Baud */,
EUartBaudRate76800/* =bitmask for 76800 Baud */,
EUartBaudRate57600/* =bitmask for 57600 Baud */,
EUartBaudRate38400/* =bitmask for 38400 Baud */,
EUartBaudRate19200/* =bitmask for 19200 Baud */,
EUartBaudRate14400/* =bitmask for 14400 Baud */,
EUartBaudRate9600/* =bitmask for 9600 Baud */,
EUartBaudRate4800/* =bitmask for 4800 Baud */,
EUartBaudRate2400/* =bitmask for 2400 Baud */,
EUartBaudRate1200/* =bitmask for 1200 Baud */,
EUartBaudRate600/* =bitmask for 600 Baud */,
EUartBaudRate300/* =bitmask for 300 Baud */,
EUartBaudRate150/* =bitmask for 150 Baud */,
EUartBaudRate110/* =bitmask for 110 Baud */
};
// EXAMPLE ONLY
enum TUartBreak
{
EUartBreakOff/* =bitmask for turning Break Off */,
EUartBreakOn/* =bitmask for turning Break On */
};
// EXAMPLE ONLY
enum TUartParity
{
EUartParityNone/* =bitmask for no Parity */,
EUartParityOdd/* =bitmask for odd Parity */,
EUartParityEven/* =bitmask for even Parity */
};
// EXAMPLE ONLY
enum TUartStopBit
{
EUartStopBitOne/* =bitmask for one stop bit */,
EUartStopBitTwo/* =bitmask for two stop bits */
};
enum TUartDataLength
{
EUartDataLength5/* =bitmask for five data bits */,
EUartDataLength6/* =bitmask for six data bits */,
EUartDataLength7/* =bitmask for seven data bits */,
EUartDataLength8/* =bitmask for eight data bits */
};
//Remove the #if block if this code needs to be used or referenced.
#if 0 //RVCT40 warning
//
// TO DO: (mandatory)
//
// Lookup table to convert EPOC baud rates into hardware-specific baud rate values
// Unsupported baud rates select the nearest lower rate.
//
// EXAMPLE ONLY
static const TUartBaudRate BaudRate[19] =
{
EUartBaudRate110,EUartBaudRate110,EUartBaudRate110,
EUartBaudRate110,EUartBaudRate150,EUartBaudRate300,
EUartBaudRate600,EUartBaudRate1200,EUartBaudRate110,
EUartBaudRate110,EUartBaudRate2400,EUartBaudRate110,
EUartBaudRate4800,EUartBaudRate110,EUartBaudRate9600,
EUartBaudRate19200,EUartBaudRate38400,EUartBaudRate57600,
EUartBaudRate115200
};
//
// TO DO: (mandatory)
//
// Lookup table to convert EPOC parity settings into hardware-specific values
//
// EXAMPLE ONLY
static const TUartParity Parity[3] =
{
EUartParityNone,EUartParityEven,EUartParityOdd
};
//
// TO DO: (mandatory)
//
// Lookup table to convert EPOC stop bit values into hardware-specific values
//
// EXAMPLE ONLY
static const TUartStopBit StopBit[2] =
{
EUartStopBitOne,EUartStopBitTwo
};
//
// TO DO: (mandatory)
//
// Lookup table to convert EPOC data bit settings into hardware-specific values
//
// EXAMPLE ONLY
static const TUartDataLength DataLength[4] =
{
EUartDataLength5,EUartDataLength6,
EUartDataLength7,EUartDataLength8
};
#endif //RVCT40 warning
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);
};
class DCommTemplate : public DComm
{
public:
DCommTemplate();
~DCommTemplate();
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;
TLinAddr iPortAddr;
TInt iInInterrupt;
TUint iSignals;
TDynamicDfcQue* iDfcQ;
};
DDriverComm::DDriverComm()
//
// Constructor
//
{
//
// TO DO: (mandatory)
//
// Set up iUnitMask with the number of Units (Serial Ports) supported by this PDD,
// 1 bit set per Unit supported e.g.:
// iUnitsMask=0x7; -> supports units 0, 1, 2
//
iVersion=TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber);
}
TInt DDriverComm::Install()
//
// Install the driver
//
{
return SetName(&KPddName);
}
void GetTemplateCommsCaps(TDes8 &aCaps, TInt aUnit)
{
TCommCaps2 capsBuf;
//
// TO DO: (mandatory)
//
// Fill in the Caps structure with the relevant information for this Unit, e.g
// TCommCapsV02 &c=capsBuf();
// c.iRate=(OR in as many KCapsBpsXXX as bit rates supported);
// c.iDataBits=(OR in as many KCapsDataXXX as data length configuration supported);
// c.iStopBits=(OR in as many KCapsStopXXX as the number of stop bits configurations supported);
// c.iParity=(OR in as many KCapsParityXXX as parity configuration supported);
// c.iHandshake=(OR in all KCapsObeyXXXSupported, KCapsSendXXXSupported, KCapsFailXXXSupported, KCapsFreeXXXSupported
// as required for this Unit's configuration);.
// c.iSignals=(OR in as many KCapsSignalXXXSupported as Modem control signals controllable by this Unit);
// c.iSIR=(0 or OR in as many KCapsSIRXXX as IR bit rates supported);
// c.iNotificationCaps=(OR in as many KNotifyXXXSupported as notifications supported by this Unit);
// c.iFifo=(0 or KCapsHasFifo);
// c.iRoleCaps=(0 or KCapsRoleSwitchSupported);
// c.iFlowControlCaps=(0 or KCapsFlowControlStatusSupported);
/** @see TCommCapsV02 */
//
aCaps.FillZ(aCaps.MaxLength());
aCaps=capsBuf.Left(Min(capsBuf.Length(),aCaps.MaxLength()));
}
void DDriverComm::GetCaps(TDes8 &aDes) const
//
// Return the drivers capabilities
//
{
GetTemplateCommsCaps(aDes, 0);
}
TInt DDriverComm::Create(DBase*& aChannel, TInt aUnit, const TDesC8* anInfo, const TVersion& aVer)
//
// Create a driver
//
{
DCommTemplate* pD=new DCommTemplate;
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 (Version and Unit)
//
{
if ((!Kern::QueryVersionSupported(iVersion,aVer)) || (!Kern::QueryVersionSupported(aVer,TVersion(KMinimumLddMajorVersion,KMinimumLddMinorVersion,KMinimumLddBuild))))
return KErrNotSupported;
//
// TO DO: (mandatory)
//
// Return KErrNotSupported if aUnit is not in the supported range for this driver, KErrNone if it is
//
return KErrNone;
}
DCommTemplate::DCommTemplate()
//
// Constructor
//
{
iInterruptId=-1; // -1 means not bound
}
DCommTemplate::~DCommTemplate()
//
// Destructor
//
{
if (iInterruptId>=0)
Interrupt::Unbind(iInterruptId);
if (iDfcQ)
{
iDfcQ->Destroy();
}
}
const TInt KDCommTemplDfcThreadPriority = 24;
_LIT(KDCommTemplDfcThread,"DCommTemplDfcThread");
TInt DCommTemplate::DoCreate(TInt aUnit, const TDesC8* /*anInfo*/)
//
// Sets up the PDD
//
{
iUnit=aUnit;
TInt irq=-1;
//
// TO DO: (mandatory)
//
// Create own DFC queue
TInt r = Kern::DynamicDfcQCreate(iDfcQ, KDCommTemplDfcThreadPriority, KDCommTemplDfcThread);
if (r != KErrNone)
return r;
// Set iPortAddr and irq with the Linear Base address of the UART and the Interrupt ID coresponding to aUnit
//
// bind to UART interrupt
r=Interrupt::Bind(irq,Isr,this);
if (r==KErrNone)
iInterruptId=irq;
//
// TO DO: (optional)
//
// Any other setting up of UART hardware registers, required for:
// - Disabling the UART operation
// - disabling all UART Interrupts
// - clearing all Rx errors
// - clearing all UART interrupts
// - de-activating output Modem Control signals
//
Variant::MarkDebugPortOff();
return r;
}
TDfcQue* DCommTemplate::DfcQ(TInt aUnit)
//
// Return the DFC queue to be used for this device
// For UARTs just use the standard low priority DFC queue
// For Serial PC cards, use the PC card controller thread for the socket in question.
//
{
return aUnit==iUnit ? iDfcQ : NULL;
}
TInt DCommTemplate::Start()
//
// Start receiving characters
//
{
iTransmitting=EFalse; // if EnableTransmit() called before Start()
//
// TO DO: (mandatory)
//
// Set up hardware registers to enable the UART and switch receive mode on
//
// if (iUnit!=IR Port) TO DO: (mandatory): Implement
{
iSignals=Signals();
iLdd->UpdateSignals(iSignals);
}
//
// TO DO: (optional)
//
// If Unit is IR Port may need to start the IR port
//
Interrupt::Enable(iInterruptId);
return KErrNone;
}
TBool FinishedTransmitting(TAny* aPtr)
{
//
// TO DO: (mandatory)
//
// Return ETrue if UART is still transmitting, EFalse Otherwise
//
return EFalse; // EXAMPLE ONLY
}
void DCommTemplate::Stop(TStopMode aMode)
//
// Stop receiving characters
//
{
switch (aMode)
{
case EStopNormal:
case EStopPwrDown:
Interrupt::Disable(iInterruptId);
iTransmitting=EFalse;
// wait for uart to stop tranmitting
Kern::PollingWait(FinishedTransmitting,this,3,100);
//
// TO DO: (optional)
//
// Any other setting up of UART hardware registers, required for:
// - Disabling the UART operation
// - disabling all UART Interrupts
// - disabling Transmit and Receive pathes
// - clearing all UART interrupts
//
break;
case EStopEmergency:
Interrupt::Disable(iInterruptId);
iTransmitting=EFalse;
break;
}
//
// TO DO: (optional)
//
// If Unit is IR Port may need to stop the IR port
//
Variant::MarkDebugPortOff();
}
void DCommTemplate::Break(TBool aState)
//
// Start or stop the uart breaking
//
{
if (aState)
{
//
// TO DO: (mandatory)
//
// Enable sending a Break (space) condition
//
}
else
{
//
// TO DO: (mandatory)
//
// Stop sending a Break (space) condition
//
}
}
void DCommTemplate::EnableTransmit()
//
// Start sending characters.
//
{
TBool tx = (TBool)__e32_atomic_swp_ord32(&iTransmitting, 1);
if (tx)
return;
TInt r = 0;
while (/* (Transmit FIFO Not full) && */ Kern::PowerGood()) // TO DO: (mandatory): Implement
{
TInt r=TransmitIsr();
if(r<0)
{
//no more to send
iTransmitting=EFalse;
break;
}
//
// TO DO: (mandatory)
//
// Write transmit character into transmit FIFO or output register
//
}
TInt irq=0;
if (!iInInterrupt) // CheckTxBuffer adds a Dfc: can only run from ISR or with NKernel locked
{
NKern::Lock();
irq=NKern::DisableAllInterrupts();
}
CheckTxBuffer();
if (!iInInterrupt)
{
NKern::RestoreInterrupts(irq);
NKern::Unlock();
}
//
// TO DO: (mandatory)
//
// Enable transmission of data
//
if (r>=0) // only enable interrupt if there's more data to send
{
//
// TO DO: (mandatory)
//
// Enable transmit interrupt in the Hardware (Interrupt::Enable() has already been called in Start())
//
}
}
TUint DCommTemplate::Signals() const
//
// Read and translate the modem lines
//
{
TUint signals=0;
//
// TO DO: (mandatory)
//
// If the UART corresponding to iUnit supports Modem Control Signals, read them and return a bitmask with one or
// more of the following OR-ed in:
// - KSignalDTR,
// - KSignalRTS,
// - KSignalDSR,
// - KSignalCTS,
// - KSignalDCD.
//
return signals;
}
void DCommTemplate::SetSignals(TUint aSetMask, TUint aClearMask)
//
// Set signals.
//
{
//
// TO DO: (mandatory)
//
// If the UART corresponding to iUnit supports Modem Control Signals, converts the flags in aSetMask and aClearMask
// into hardware-specific bitmasks to write to the UART modem/handshake output register(s).
// aSetMask, aClearMask will have one or more of the following OR-ed in:
// - KSignalDTR,
// - KSignalRTS,
//
}
TInt DCommTemplate::ValidateConfig(const TCommConfigV01 &aConfig) const
//
// Check a config structure.
//
{
//
// TO DO: (mandatory)
//
// Checks the the options in aConfig are supported by the UART corresponding to iUnit
// May need to check:
// - aConfig.iParity (contains one of EParityXXX)
/** @see TParity */
// - aConfig.iRate (contains one of EBpsXXX)
/** @see TBps */
// - aConfig.iDataBits (contains one of EDataXXX)
/** @see TDataBits */
// - aConfig.iStopBits (contains one of EStopXXX)
/** @see TDataBits */
// - aConfig.iHandshake (contains one of KConfigObeyXXX or KConfigSendXXX or KConfigFailXXX or KConfigFreeXXX)
// - aConfig.iParityError (contains KConfigParityErrorFail or KConfigParityErrorIgnore or KConfigParityErrorReplaceChar)
// - aConfig.iFifo (contains ether EFifoEnable or EFifoDisable)
/** @see TFifo */
// - aConfig.iSpecialRate (may contain a rate not listed under TBps)
// - aConfig.iTerminatorCount (conatains number of special characters used as terminators)
// - aConfig.iTerminator[] (contains a list of special characters which can be used as terminators)
// - aConfig.iXonChar (contains the character used as XON - software flow control)
// - aConfig.iXoffChar (contains the character used as XOFF - software flow control)
// - aConfig.iParityErrorChar (contains the character used to replace bytes received with a parity error)
// - aConfig.iSIREnable (contains either ESIREnable or ESIRDisable)
/** @see TSir */
// - aConfig.iSIRSettings (contains one of KConfigSIRXXX)
// and returns KErrNotSupported if the UART corresponding to iUnit does not support this configuration
//
return KErrNone;
}
void DCommTemplate::CheckConfig(TCommConfigV01& aConfig)
{
//
// TO DO: (optional)
//
// Validates the default configuration that is defined when a channel is first opened
//
}
TInt DCommTemplate::DisableIrqs()
//
// Disable normal interrupts
//
{
return NKern::DisableInterrupts(1);
}
void DCommTemplate::RestoreIrqs(TInt aLevel)
//
// Restore normal interrupts
//
{
NKern::RestoreInterrupts(aLevel);
}
void DCommTemplate::Configure(TCommConfigV01 &aConfig)
//
// Configure the UART from aConfig
//
{
Kern::PollingWait(FinishedTransmitting,this,3,100); // wait for uart to stop tranmitting
//
// TO DO: (optional)
//
// Ensure Tx, Rx and the UART are disabled and disable sending Break (space) condition.
// May need to modify clocks settings, pin functions etc.
//
//
// TO DO: (mandatory)
//
// Set communications parameters such as:
// - Baud rate
// - Parity
// - Stop bits
// - Data bits
// These can be obtained from aConfig using the look-up tables above, e.g.
// TUint baudRate=BaudRate[aConfig.iRate];
// TUint parity=Parity[aConfig.iParity];
// TUint stopBits=StopBit[aConfig.iStopBits];
// TUint dataBits=DataLength[aConfig.iDataBits];
// Write these to the appropriate hardware registers using iPortAddr to identify which ste of register to modify
//
//
// TO DO: (optional)
//
// If the UART corresponding to iUnit supports IR may need to set up IR transceiver
//
}
void DCommTemplate::Caps(TDes8 &aCaps) const
//
// return our caps
//
{
GetTemplateCommsCaps(aCaps,iUnit);
}
void DCommTemplate::Isr(TAny* aPtr)
//
// Service the UART interrupt
//
{
DCommTemplate& d=*(DCommTemplate*)aPtr;
d.iInInterrupt=1; // going in...
// TUint portAddr=d.iPortAddr; TO DO: (mandatory): Uncomment this
//
// TO DO: (mandatory)
//
// Read the interrupt source register to determine if it is a Receive, Transmit or Modem Signals change interrupt.
// If required also, clear interrupts at the source.
// Then process the interrupt condition as in the following pseudo-code extract:
//
// if((Received character Interrupts) || (Error in received character Interupt)) TO DO: (mandatory): Implement
{
TUint rx[32];
TUint xon=d.iLdd->iRxXonChar;
TUint xoff=d.iLdd->iRxXoffChar;
TInt rxi=0;
TInt x=0;
TUint ch=0;
// while((Receive FIFO not empty) && Kern::PowerGood()) TO DO: (mandatory): Implement
{
TUint regStatus1=0;
// NOTE: for some hardware the order the following 2 operations is performed may have to be reversed
// if(Error in received character interrupt) TO DO: (mandatory): Implement
// regStatus1=(Read receive error bitmask off appropriate register);
// ch=(Read received character);
// coverity[dead_error_condition]
// The next line should be reachable when this template file is edited for use
if(regStatus1!=0) // if error in this character
{
// if (ch & (Parity Error)) TO DO: (mandatory): Implement
ch|=KReceiveIsrParityError;
// if (ch & (Framing Error)) TO DO: (mandatory): Implement
ch|=KReceiveIsrFrameError;
// if (ch & (Overrun)) TO DO: (mandatory): Implement
ch|=KReceiveIsrOverrunError;
}
if (ch==xon)
x=1;
else if (ch==xoff)
x=-1;
else
rx[rxi++]=ch;
}
d.ReceiveIsr(rx,rxi,x);
}
// if((Transmitted character Interrupt)) TO DO: (mandatory): Implement
{
while(/* (Transmit FIFO Not full) && */ Kern::PowerGood()) // TO DO: (mandatory): Implement
{
TInt r=d.TransmitIsr();
if(r<0)
{
//no more to send
//
// TO DO: (mandatory)
//
// Disable the Transmit Interrupt in Hardware
d.iTransmitting=EFalse;
break;
}
// (write transmit character to output FIFO or Data register) TO DO: (mandatory): Implement
}
d.CheckTxBuffer();
}
// if((Modem Signals changed Interrupt)) TO DO: (mandatory): Implement
{
TUint signals=d.Signals()&KDTEInputSignals;
if (signals != d.iSignals)
{
d.iSignals=signals;
d.iLdd->StateIsr(signals);
}
}
d.iInInterrupt=0; // going out...
}
DECLARE_STANDARD_PDD()
{
return new DDriverComm;
}