/*
* Copyright (c) 1997-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:
*
*/
#include <acminterface.h>
#include <usb/usblogger.h>
#include "AcmPort.h"
#include "AcmPortFactory.h"
#include "AcmUtils.h"
#include "AcmWriter.h"
#include "AcmReader.h"
#include "AcmPanic.h"
#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, "ECACM");
#endif
const TUint KCapsRate=( KCapsBps50
| KCapsBps75
| KCapsBps110
| KCapsBps134
| KCapsBps150
| KCapsBps300
| KCapsBps600
| KCapsBps1200
| KCapsBps1800
| KCapsBps2000
| KCapsBps2400
| KCapsBps3600
| KCapsBps4800
| KCapsBps7200
| KCapsBps9600
| KCapsBps19200
| KCapsBps38400
| KCapsBps57600
| KCapsBps115200
| KCapsBps230400
| KCapsBps460800
| KCapsBps576000
| KCapsBps115200
| KCapsBps4000000 );
const TUint KCapsDataBits=KCapsData8;
const TUint KCapsStopBits=KCapsStop1;
const TUint KCapsParity=KCapsParityNone;
const TUint KCapsHandshaking=(
KCapsSignalCTSSupported
| KCapsSignalDSRSupported
| KCapsSignalRTSSupported
| KCapsSignalDTRSupported);
const TUint KCapsSignals=(
KCapsSignalCTSSupported
| KCapsSignalDSRSupported
| KCapsSignalRTSSupported
| KCapsSignalDTRSupported);
const TUint KCapsFifo = 0;
const TUint KCapsSIR = 0; ///< Serial Infrared capabilities
const TUint KCapsNotification = KNotifySignalsChangeSupported;
const TUint KCapsRoles = 0;
const TUint KCapsFlowControl = 0;
const TBps KDefaultDataRate = EBps115200;
const TDataBits KDefaultDataBits = EData8;
const TParity KDefaultParity = EParityNone;
const TStopBits KDefaultStopBits = EStop1;
const TUint KDefaultHandshake = 0;
///< Default XON character is <ctrl>Q
const TText8 KDefaultXon = 17;
///< Default XOFF character is <ctrl>S
const TText8 KDefaultXoff = 19;
// Starting size of each of the receive and transmit buffers.
const TUint KDefaultBufferSize = 0x1000;
CAcmPort* CAcmPort::NewL(const TUint aUnit, MAcmPortObserver& aFactory)
/**
* Make a new CPort for the comm server
*
* @param aFactory The observer of the port, to be notified when the port
* closes.
* @param aUnit The port number.
* @return Ownership of a newly created CAcmPort object
*/
{
LOG_STATIC_FUNC_ENTRY
CAcmPort* self = new(ELeave) CAcmPort(aUnit, aFactory);
CleanupClosePushL(*self);
self->ConstructL();
CleanupStack::Pop();
return self;
}
void CAcmPort::ConstructL()
/**
* Standard second phase constructor. Create owned classes and initialise the
* port.
*/
{
iReader = CAcmReader::NewL(*this, KDefaultBufferSize);
iWriter = CAcmWriter::NewL(*this, KDefaultBufferSize);
TName name;
name.Num(iUnit);
LEAVEIFERRORL(SetName(&name));
iCommServerConfig.iBufFlags = 0;
iCommServerConfig.iBufSize = iReader->BufSize();
iCommConfig.iRate = KDefaultDataRate;
iCommConfig.iDataBits = KDefaultDataBits;
iCommConfig.iParity = KDefaultParity;
iCommConfig.iStopBits = KDefaultStopBits;
iCommConfig.iHandshake = KDefaultHandshake;
iCommConfig.iParityError = 0;
iCommConfig.iFifo = 0;
iCommConfig.iSpecialRate = 0;
iCommConfig.iTerminatorCount= 0;
iCommConfig.iXonChar = KDefaultXon;
iCommConfig.iXoffChar = KDefaultXoff;
iCommConfig.iParityErrorChar= 0;
iCommConfig.iSIREnable = ESIRDisable;
iCommConfig.iSIRSettings = 0;
}
CAcmPort::CAcmPort(const TUint aUnit, MAcmPortObserver& aObserver)
: iCommNotification(iCommNotificationDes()),
iObserver(aObserver),
iUnit(aUnit)
/**
* Constructor.
*
* @param aObserver The observer of the port, to be notified when the port
* closes.
* @param aUnit The port number.
*/
{
}
void CAcmPort::StartRead(const TAny* aClientBuffer, TInt aLength)
/**
* Downcall from C32. Queue a read.
*
* @param aClientBuffer pointer to the client's buffer
* @param aLength number of bytes to read
*/
{
LOG_LINE
LOG_FUNC
LOGTEXT3(_L8("\taClientBuffer=0x%08x, aLength=%d"),
aClientBuffer, aLength);
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
ReadCompleted(KErrAccessDenied);
return;
}
// Analyse the request and call the relevant API on the data reader. NB We
// do not pass requests for zero bytes to the data reader. They are an
// RComm oddity we should handle here.
__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
if(iReader->IsNotifyDataAvailableQueryPending())
{
ReadCompleted(KErrInUse);
return;
}
if ( aLength < 0 )
{
iReader->ReadOneOrMore(aClientBuffer, static_cast<TUint>(-aLength));
}
else if ( aLength > 0 )
{
iReader->Read(aClientBuffer, static_cast<TUint>(aLength));
}
else
{
// Obscure RComm API feature- complete zero-length Read immediately,
// to indicate that the hardware is powered up.
LOGTEXT(_L8("\tcompleting immediately with KErrNone"));
ReadCompleted(KErrNone);
}
}
void CAcmPort::ReadCancel()
/**
* Downcall from C32. Cancel a read.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return;
}
__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
iReader->ReadCancel();
}
TInt CAcmPort::QueryReceiveBuffer(TInt& aLength) const
/**
* Downcall from C32. Get the amount of data in the receive buffer.
*
* @param aLength reference to where the amount will be written to
* @return Error.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
aLength = static_cast<TInt>(iReader->BufLen());
LOGTEXT2(_L8("\tlength=%d"), aLength);
return KErrNone;
}
void CAcmPort::ResetBuffers(TUint aFlags)
/**
* Downcall from C32. Reset zero or more of the Tx and Rx buffers.
*
* @param aFlags Flags indicating which buffer(s) to reset.
*/
{
LOG_LINE
LOGTEXT2(_L8(">>CAcmPort::ResetBuffers aFlags = %d"), aFlags);
if ( aFlags & KCommResetRx )
{
LOGTEXT(_L8("\tresetting Rx buffer"));
__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
iReader->ResetBuffer();
}
if ( aFlags & KCommResetTx )
{
LOGTEXT(_L8("\tresetting Tx buffer"));
__ASSERT_DEBUG(iWriter, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
iWriter->ResetBuffer();
}
LOGTEXT(_L8("<<CAcmPort::ResetBuffers"));
}
void CAcmPort::StartWrite(const TAny* aClientBuffer, TInt aLength)
/**
* Downcall from C32. Queue a write
*
* @param aClientBuffer pointer to the Client's buffer
* @param aLength number of bytes to write
*/
{
LOG_LINE
LOG_FUNC
LOGTEXT3(_L8("\taClientBuffer=0x%08x, aLength=%d"),
aClientBuffer, aLength);
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
WriteCompleted(KErrAccessDenied);
return;
}
if ( aLength < 0 )
{
// Negative length makes no sense.
LOGTEXT(_L8("\taLength is negative- "
"completing immediately with KErrArgument"));
WriteCompleted(KErrArgument);
return;
}
// NB We pass zero-byte writes down to the LDD as normal. This results in
// a zero-length packet being sent to the host.
__ASSERT_DEBUG(iWriter, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
iWriter->Write(aClientBuffer, static_cast<TUint>(aLength));
}
void CAcmPort::WriteCancel()
/**
* Downcall from C32. Cancel a pending write
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return;
}
__ASSERT_DEBUG(iWriter, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
iWriter->WriteCancel();
}
void CAcmPort::Break(TInt aTime)
/**
* Downcall from C32. Send a break signal to the host.
* Note that it is assumed that the NotifyBreak() request only applies to
* break signals sent from the host.
*
* @param aTime Length of break in microseconds
*/
{
LOG_LINE
LOG_FUNC
LOGTEXT2(_L8("\taTime=%d (microseconds)"), aTime);
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return;
}
iBreak = ETrue;
iCancellingBreak = EFalse;
// Use the time value directly as microseconds are given. We are a USB
// 'device' so the RComm client is always on the device side.
TInt err = iAcm->BreakRequest(CBreakController::EDevice,
CBreakController::ETiming,
TTimeIntervalMicroSeconds32(aTime));
LOGTEXT2(_L8("\tBreakRequest = %d"), err);
// Note that the break controller may refuse our request if a host-driven
// break is outstanding.
if ( err )
{
LOGTEXT2(_L8("\tcalling BreakCompleted with %d"), err);
iBreak = EFalse;
BreakCompleted(err);
}
}
void CAcmPort::BreakCancel()
/**
* Downcall from C32. Cancel a pending break.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return;
}
iCancellingBreak = ETrue;
TInt err = iAcm->BreakRequest(CBreakController::EDevice,
CBreakController::EInactive);
// Note that the device cannot turn off a break if there's a host-driven
// break in progress.
LOGTEXT2(_L8("\tBreakRequest = %d"), err);
if ( err )
{
iCancellingBreak = EFalse;
}
// Whether BreakOff worked or not, reset our flag saying we're no longer
// interested in any subsequent completion anyway.
iBreak = EFalse;
}
TInt CAcmPort::GetConfig(TDes8& aDes) const
/**
* Downcall from C32. Pass a config request. Only supports V01.
*
* @param aDes config will be written to this descriptor
* @return Error.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
if ( aDes.Length() < static_cast<TInt>(sizeof(TCommConfigV01)) )
{
LOGTEXT(_L8("\t***not supported"));
return KErrNotSupported;
}
TCommConfig commConfigPckg;
TCommConfigV01& commConfig = commConfigPckg();
commConfig = iCommConfig;
aDes.Copy(commConfigPckg);
LOGTEXT2(_L8("\tiCommConfig.iRate = %d"), iCommConfig.iRate);
LOGTEXT2(_L8("\tiCommConfig.iDataBits = %d"), iCommConfig.iDataBits);
LOGTEXT2(_L8("\tiCommConfig.iStopBits = %d"), iCommConfig.iStopBits);
LOGTEXT2(_L8("\tiCommConfig.iParity = %d"), iCommConfig.iParity);
LOGTEXT2(_L8("\tiCommConfig.iHandshake = %d"), iCommConfig.iHandshake);
LOGTEXT2(_L8("\tiCommConfig.iParityError = %d"),
iCommConfig.iParityError);
LOGTEXT2(_L8("\tiCommConfig.iFifo = %d"), iCommConfig.iFifo);
LOGTEXT2(_L8("\tiCommConfig.iSpecialRate = %d"),
iCommConfig.iSpecialRate);
LOGTEXT2(_L8("\tiCommConfig.iTerminatorCount = %d"),
iCommConfig.iTerminatorCount);
LOGTEXT2(_L8("\tiCommConfig.iSIREnable = %d"), iCommConfig.iSIREnable);
LOGTEXT2(_L8("\tiCommConfig.iSIRSettings = %d"),
iCommConfig.iSIRSettings);
return KErrNone;
}
TInt CAcmPort::SetConfig(const TDesC8& aDes)
/**
* Downcall from C32. Set config on the port using a TCommConfigV01 package.
*
* @param aDes descriptor containing the new config
* @return Error
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
LOGTEXT4(_L8("\tlength of argument %d, TCommConfigV01 %d, "
"TCommConfigV02 %d"),
aDes.Length(),
(TInt)sizeof(TCommConfigV01),
(TInt)sizeof(TCommConfigV02));
if ( aDes.Length() < static_cast<TInt>(sizeof(TCommConfigV01)) )
{
LOGTEXT(_L8("\t***not supported"));
return KErrNotSupported;
}
TCommConfig configPckg;
configPckg.Copy(aDes);
TCommConfigV01& config = configPckg();
LOGTEXT2(_L8("\tiCommConfig.iRate = %d"), config.iRate);
LOGTEXT2(_L8("\tiCommConfig.iDataBits = %d"), config.iDataBits);
LOGTEXT2(_L8("\tiCommConfig.iStopBits = %d"), config.iStopBits);
LOGTEXT2(_L8("\tiCommConfig.iParity = %d"), config.iParity);
LOGTEXT2(_L8("\tiCommConfig.iHandshake = %d"), config.iHandshake);
LOGTEXT2(_L8("\tiCommConfig.iParityError = %d"), config.iParityError);
LOGTEXT2(_L8("\tiCommConfig.iFifo = %d"), config.iFifo);
LOGTEXT2(_L8("\tiCommConfig.iSpecialRate = %d"), config.iSpecialRate);
LOGTEXT2(_L8("\tiCommConfig.iTerminatorCount = %d"),
config.iTerminatorCount);
LOGTEXT2(_L8("\tiCommConfig.iSIREnable = %d"), config.iSIREnable);
LOGTEXT2(_L8("\tiCommConfig.iSIRSettings = %d"), config.iSIRSettings);
// Tell the reader object about the new terminators. Pass the whole config
// struct by reference for ease.
iReader->SetTerminators(config);
HandleConfigNotification(config.iRate,
config.iDataBits,
config.iParity,
config.iStopBits,
config.iHandshake);
iCommConfig = config;
return KErrNone;
}
void CAcmPort::HandleConfigNotification(TBps aRate,
TDataBits aDataBits,
TParity aParity,
TStopBits aStopBits,
TUint aHandshake)
/**
* Construct and complete the config notification data structure.
* @param aRate New data rate
* @param aDataBits New number of data bits
* @param aParity New parity check setting
* @param aStopBits New stop bit setting
* @param aHandshake New handshake setting
*/
{
LOGTEXT(_L8(">>CAcmPort::HandleConfigNotification"));
LOGTEXT2(_L8("\taRate = %d"), aRate);
LOGTEXT2(_L8("\taDataBits = %d"), aDataBits);
LOGTEXT2(_L8("\taStopBits = %d"), aStopBits);
LOGTEXT2(_L8("\taParity = %d"), aParity);
LOGTEXT2(_L8("\taHandshake = %d"), aHandshake);
iCommNotification.iChangedMembers = 0;
iCommNotification.iRate = aRate;
if ( iCommConfig.iRate != aRate )
{
iCommNotification.iChangedMembers |= KRateChanged;
}
iCommNotification.iDataBits = aDataBits;
if ( iCommConfig.iDataBits != aDataBits )
{
iCommNotification.iChangedMembers |= KDataFormatChanged;
}
iCommNotification.iParity = aParity;
if ( iCommConfig.iParity != aParity )
{
iCommNotification.iChangedMembers |= KDataFormatChanged;
}
iCommNotification.iStopBits = aStopBits;
if ( iCommConfig.iStopBits != aStopBits )
{
iCommNotification.iChangedMembers |= KDataFormatChanged;
}
iCommNotification.iHandshake = aHandshake;
if ( iCommConfig.iHandshake != aHandshake )
{
iCommNotification.iChangedMembers |= KHandshakeChanged;
}
if ( iCommNotification.iChangedMembers )
{
LOGTEXT(_L8("\tcalling ConfigChangeCompleted with KErrNone"));
ConfigChangeCompleted(iCommNotificationDes,KErrNone);
iNotifyConfigChange = EFalse;
}
LOGTEXT(_L8("<<CAcmPort::HandleConfigNotification"));
}
TInt CAcmPort::SetServerConfig(const TDesC8& aDes)
/**
* Downcall from C32. Set server config using a TCommServerConfigV01 package.
*
* @param aDes package with new server configurations
* @return Error.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
if ( aDes.Length() < static_cast<TInt>(sizeof(TCommServerConfigV01)) )
{
LOGTEXT(_L8("\t***not supported"));
return KErrNotSupported;
}
TCommServerConfig serverConfigPckg;
serverConfigPckg.Copy(aDes);
TCommServerConfigV01& serverConfig = serverConfigPckg();
if ( serverConfig.iBufFlags != KCommBufferFull )
{
LOGTEXT(_L8("\t***not supported"));
return KErrNotSupported;
}
// Apply the buffer size setting to Rx and Tx buffers.
TInt err = DoSetBufferLengths(serverConfig.iBufSize);
if ( err )
{
// Failure- the buffer lengths will have been left as they were, so
// just return error.
LOGTEXT2(_L8("\t***DoSetBufferLengths=%d"), err);
return err;
}
// Changed buffer sizes OK. Note that new config.
iCommServerConfig = serverConfig;
return KErrNone;
}
TInt CAcmPort::GetServerConfig(TDes8& aDes)
/**
* Downcall from C32. Get the server configs. Only supports V01.
*
* @param aDes server configs will be written to this descriptor
* @return Error.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
if ( aDes.Length() < static_cast<TInt>(sizeof(TCommServerConfigV01)) )
{
LOGTEXT(_L8("\t***not supported"));
return KErrNotSupported;
}
TCommServerConfig* serverConfigPckg =
reinterpret_cast<TCommServerConfig*>(&aDes);
TCommServerConfigV01& serverConfig = (*serverConfigPckg)();
serverConfig = iCommServerConfig;
return KErrNone;
}
TInt CAcmPort::GetCaps(TDes8& aDes)
/**
* Downcall from C32. Get the port's capabilities. Supports V01 and V02.
*
* @param aDes caps will be written to this descriptor
* @return Error.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
TCommCaps2 capsPckg;
TCommCapsV02& caps = capsPckg();
switch ( aDes.Length() )
{
case sizeof(TCommCapsV02):
{
caps.iNotificationCaps = (KCapsNotification | KNotifyDataAvailableSupported);
caps.iRoleCaps = KCapsRoles;
caps.iFlowControlCaps = KCapsFlowControl;
}
// no break is deliberate
case sizeof(TCommCapsV01):
{
caps.iRate = KCapsRate;
caps.iDataBits = KCapsDataBits;
caps.iStopBits = KCapsStopBits;
caps.iParity = KCapsParity;
caps.iHandshake = KCapsHandshaking;
caps.iSignals = KCapsSignals;
caps.iFifo = KCapsFifo;
caps.iSIR = KCapsSIR;
}
break;
default:
{
LOGTEXT(_L8("\t***bad argument"));
return KErrArgument;
}
}
aDes.Copy(capsPckg.Left(aDes.Length()));
return KErrNone;
}
TInt CAcmPort::GetSignals(TUint& aSignals)
/**
* Downcall from C32. Get the status of the signal pins
*
* @param aSignals signals will be written to this descriptor
* @return Error.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
aSignals = ConvertSignals(iSignals);
LOGTEXT3(_L8("iSignals=0x%x, aSignals=0x%x"),
iSignals, aSignals);
return KErrNone;
}
TInt CAcmPort::SetSignalsToMark(TUint aSignals)
/**
* Downcall from C32. Set selected signals to high (logical 1)
*
* @param aSignals bitmask with the signals to be set
* @return Error.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
TUint32 newSignals = iSignals | ConvertAndFilterSignals(aSignals);
TInt err = SetSignals(newSignals);
if ( err )
{
LOGTEXT2(_L8("***SetSignals = %d"), err);
return err;
}
LOGTEXT3(_L8("iSignals=0x%x, aSignals=0x%x"),
iSignals, aSignals);
return KErrNone;
}
TInt CAcmPort::SetSignalsToSpace(TUint aSignals)
/**
* Downcall from C32. Set selected signals to low (logical 0)
*
* @param aSignals bitmask with the signals to be cleared
* @return Error.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
TUint32 newSignals = iSignals & ~ConvertAndFilterSignals(aSignals);
TInt err = SetSignals(newSignals);
if ( err )
{
LOGTEXT2(_L8("***SetSignals = %d"), err);
return err;
}
LOGTEXT3(_L8("iSignals=0x%x, aSignals=0x%x"),
iSignals, aSignals);
return KErrNone;
}
TInt CAcmPort::SetSignals(TUint32 aNewSignals)
/**
* Handle a request by the client to change signal state. This function pushes
* the change to the host, completes any outstanding notifications, and stores
* the new signals in iSignals if there are no errors.
*
* @param aSignals bitmask with the signals to be set
* @return Error.
*/
{
LOG_FUNC
TBool ring =((aNewSignals & KSignalRNG)==KSignalRNG);
TBool dsr =((aNewSignals & KSignalDSR)==KSignalDSR);
TBool dcd =((aNewSignals & KSignalDCD)==KSignalDCD);
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
TInt err = iAcm->SendSerialState(ring,dsr,dcd);
if ( err )
{
LOGTEXT2(_L8("\t***SendSerialState = %d- returning"), err);
return err;
}
// Report the new signals if they have changed and notification mask shows
// the upper layer wanted to be sensitive to these signals
if ( iNotifySignalChange )
{
TUint32 changedSignals = ( iSignals ^ aNewSignals ) & iNotifySignalMask;
if ( changedSignals != 0 )
{
// Mark which signals have changed
// Could do something like:
// changedSignalsMask = ( changedSignals & (KSignalRNG|KSignalDCD|KSignalDSR) ) * KSignalChanged
// but this relies on the how the signal constants are formed in d32comm.h
TUint32 changedSignalsMask = 0;
if ( ( changedSignals & KSignalRNG ) != 0 )
{
changedSignalsMask |= KRNGChanged;
}
if ( ( changedSignals & KSignalDCD ) != 0 )
{
changedSignalsMask |= KDCDChanged;
}
if ( ( changedSignals & KSignalDSR ) != 0 )
{
changedSignalsMask |= KDSRChanged;
}
// Report correctly mapped signals that client asked for
LOGTEXT(_L8("\tcalling SignalChangeCompleted with KErrNone"));
TUint32 signals = ConvertSignals ( changedSignalsMask | ( aNewSignals & iNotifySignalMask ) );
SignalChangeCompleted ( signals, KErrNone );
iNotifySignalChange = EFalse;
}
}
// Store new signals in iSignals
iSignals = aNewSignals;
return KErrNone;
}
TUint32 CAcmPort::ConvertAndFilterSignals(TUint32 aSignals) const
/**
* This function maps from signals used for the port's current role
* to the DCE signals used internally, filtering out signals that are
* inputs for the current role.
*
* This would be used, for example, to parse the signals specified by the
* client for a "SetSignals" request before storage in the iSignals member
* variable (always DCE).
*
* @param aSignals Input signals.
* @return The signals, with the lines switched round according to the port's
* role.
*/
{
LOG_FUNC
TUint32 signals = 0;
// note that this never allows the client to use this method
// to diddle the BREAK state
// handle role-specific mapping
if ( iRole == ECommRoleDTE )
{
// Ignore DSR, DCD, RI and CTS as these are DTE inputs.
// Map DTR to DSR
if((aSignals&KSignalDTR)==KSignalDTR)
{
signals|=KSignalDSR;
}
// Map RTS to CTS
if((aSignals&KSignalRTS)==KSignalRTS)
{
signals|=KSignalCTS;
}
}
else // iRole==EDce
{
// Ignore RTS and DTR as these are DCE inputs and therefore cannot be set.
// Keep mapping of DSR, CTS, DCD and RI.
signals |= aSignals &
( KSignalDSR
| KSignalCTS
| KSignalDCD
| KSignalRNG);
}
return signals;
}
TUint32 CAcmPort::ConvertSignals(TUint32 aSignals) const
/**
* This function converts the given signals between the internal mapping
* and the client mapping, taking into account the port's currnt role.
* As the mapping is one to one, the input can be either the client or
* internal signals.
*
* This would be used, for example, to parse the signals being sent to the
* client in a "GetSignals" request, or to parse the signals sent from the
* client when setting up a notification.
*
* It should not be used for "SetSignals", as it does not take into account
* what signals are inputs or outputs for the current role.
*
* @param aSignals Input signals (internal or client).
* @return Output signals.
* port is.
*/
{
LOG_FUNC
// Swap signals around if the client is expecting DTE signalling
if ( iRole == ECommRoleDTE )
{
TUint32 swappedSignals = 0;
// Map DSR to DTR
if ( ( aSignals & KSignalDSR ) == KSignalDSR )
{
swappedSignals |= KSignalDTR;
}
if ( ( aSignals & KSignalDTR ) == KSignalDTR )
{
swappedSignals |= KSignalDSR;
}
if ( ( aSignals & KDSRChanged ) != 0 )
{
swappedSignals |= KDTRChanged;
}
if ( ( aSignals & KDTRChanged ) != 0 )
{
swappedSignals |= KDSRChanged;
}
// Map CTS to RTS
if ( ( aSignals&KSignalCTS ) == KSignalCTS )
{
swappedSignals|=KSignalRTS;
}
if ( ( aSignals&KSignalRTS ) == KSignalRTS )
{
swappedSignals|=KSignalCTS;
}
if ( ( aSignals & KCTSChanged ) != 0 )
{
swappedSignals |= KRTSChanged;
}
if ( ( aSignals & KRTSChanged ) != 0 )
{
swappedSignals |= KCTSChanged;
}
// Put remapped signals back in
const TUint32 KSwapSignals = KSignalDSR | KSignalDTR | KSignalCTS | KSignalRTS
| KDSRChanged | KDTRChanged | KCTSChanged | KRTSChanged;
aSignals = ( aSignals & ~KSwapSignals ) | swappedSignals;
}
return aSignals;
}
TInt CAcmPort::GetReceiveBufferLength(TInt& aLength) const
/**
* Downcall from C32. Get size of Rx buffer. Note that the Rx and Tx
* buffers are assumed by the RComm API to always have the same size.
* We don't check here, but the value returned should be the size of the Tx
* buffer also.
*
* @param aLength reference to where the length will be written to
* @return Error.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
aLength = static_cast<TInt>(iReader->BufSize());
LOGTEXT2(_L8("\tlength=%d"), aLength);
return KErrNone;
}
TInt CAcmPort::DoSetBufferLengths(TUint aLength)
/**
* Utility to set the sizes of the Rx and Tx buffers. On failure, the buffers
* will be left at the size they were.
*
* @param aLength The size required.
* @return Error.
*/
{
LOG_FUNC
LOGTEXT2(_L8("\taLength=%d"), aLength);
__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
// Sart trying to resize buffers. Start with the reader.
// Before we start though, set some stuff up we may need later (see
// comments below).
const TUint oldSize = iReader->BufSize();
HBufC* dummyBuffer = HBufC::New(static_cast<TInt>(oldSize));
if ( !dummyBuffer )
{
// If we can't allocate the dummy buffer, we can't guarantee that we
// can roll back this API safely if it fails halfway through. So abort
// the entire operation.
LOGTEXT(_L8("\t***failed to allocate dummy buffer- "
"returning KErrNoMemory"));
return KErrNoMemory;
}
TInt ret = iReader->SetBufSize(aLength);
if ( ret )
{
// A failure in SetBufSize will have maintained the old buffer
// (i.e. at its old size). This is a safe failure case- simply
// clean up and return the error to the client.
delete dummyBuffer;
LOGTEXT2(_L8("\t***SetBufSize on reader failed with %d"), ret);
return ret;
}
// OK, the Rx buffer has been resized, now for the Tx buffer...
__ASSERT_DEBUG(iWriter, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
ret = iWriter->SetBufSize(aLength);
if ( ret )
{
// Failure! The Tx buffer is still safe, at its old size. However,
// to maintain the integrity of the API we need to reset the Rx
// buffer back to the old size. To make sure that this will work, free
// the dummy buffer we initially allocated.
delete dummyBuffer;
LOGTEXT2(_L8("\t***SetBufSize on writer failed with %d"), ret);
TInt err = iReader->SetBufSize(oldSize);
__ASSERT_DEBUG(!err, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
static_cast<void>(err);
// Now both buffers are at the size they started at, and the
// request failed with error code 'ret'.
return ret;
}
// We seem to have successfully resized both buffers... clean up and
// return.
delete dummyBuffer;
LOGTEXT(_L8("\treturning KErrNone"));
return KErrNone;
}
TInt CAcmPort::SetReceiveBufferLength(TInt aLength)
/**
* Downcall from C32. Set the size of Rx buffer. Note that the Rx and Tx
* buffers are assumed by the RComm API to always have the same size.
* Therefore set the size of the Tx buffer also.
*
* @param aLength new length of Tx and Rx buffer
* @return Error.
*/
{
LOG_LINE
LOG_FUNC
LOGTEXT2(_L8("\taLength=%d"), aLength);
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
TInt ret = DoSetBufferLengths(static_cast<TUint>(aLength));
LOGTEXT2(_L8("\tDoSetBufferLengths=%d"), ret);
LOGTEXT2(_L8("\treturning %d"), ret);
return ret;
}
void CAcmPort::Destruct()
/**
* Downcall from C32. Destruct - we must (eventually) call delete this
*/
{
LOG_FUNC
delete this;
}
void CAcmPort::FreeMemory()
/**
* Downcall from C32. Attempt to reduce our memory foot print.
* Implemented to do nothing.
*/
{
LOG_LINE
LOG_FUNC
}
void CAcmPort::NotifyDataAvailable()
/**
* Downcall from C32. Notify client when data is available.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
NotifyDataAvailableCompleted(KErrAccessDenied);
return;
}
__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
iReader->NotifyDataAvailable();
}
void CAcmPort::NotifyDataAvailableCancel()
/**
* Downcall from C32. Cancel an outstanding data available notification. Note
* that C32 actually completes the client's request for us.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return;
}
__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
iReader->NotifyDataAvailableCancel();
}
TInt CAcmPort::GetFlowControlStatus(TFlowControl& /*aFlowControl*/)
/**
* Downcall from C32. Get the flow control status. NB This is not supported.
*
* @param aFlowControl flow control status will be written to this descriptor
* @return Error.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
LOGTEXT(_L8("\t***not supported"));
return KErrNotSupported;
}
void CAcmPort::NotifyOutputEmpty()
/**
* Downcall from C32. Notify the client when the output buffer is empty. Note
* that this is not supported. Write requests only complete when the data has
* been sent to the LDD (KConfigWriteBufferedComplete is not supported) so
* it's not very useful.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
NotifyOutputEmptyCompleted(KErrAccessDenied);
return;
}
LOGTEXT(_L8("\t***not supported"));
NotifyOutputEmptyCompleted(KErrNotSupported);
}
void CAcmPort::NotifyOutputEmptyCancel()
/**
* Downcall from C32. Cancel a pending request to be notified when the output
* buffer is empty. Note that C32 actually completes the client's request for
* us.
*/
{
LOG_LINE
LOG_FUNC
}
void CAcmPort::NotifyBreak()
/**
* Downcall from C32. Notify a client of a host-driven break on the serial
* line.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
BreakNotifyCompleted(KErrAccessDenied);
return;
}
// C32 protects us against this.
__ASSERT_DEBUG(!iNotifyBreak,
_USB_PANIC(KAcmPanicCat, EPanicInternalError));
iNotifyBreak = ETrue;
}
void CAcmPort::NotifyBreakCancel()
/**
* Downcall from C32. Cancel a pending notification of a serial line break.
* Note that C32 actually completes the client's request for us.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return;
}
iNotifyBreak = EFalse;
}
void CAcmPort::BreakRequestCompleted()
/**
* Callback for completion of a break request.
* This is called when the break state next goes inactive after a break has
* been requested from the ACM port.
* This is implemented to complete the RComm client's Break request.
*/
{
LOG_FUNC
if ( !iCancellingBreak )
{
LOGTEXT(_L8("\tcalling BreakCompleted with KErrNone"));
BreakCompleted(KErrNone);
}
else
{
LOGTEXT(_L8("\tbreak is being cancelled- "
"letting C32 complete the request"));
}
// In the event of an RComm::BreakCancel, this function is called
// sychronously with CAcmPort::BreakCancel, and C32 will complete the
// original RComm::Break request with KErrCancel when that returns. So we
// only need to complete it if there isn't a cancel going on.
iBreak = EFalse;
iCancellingBreak = EFalse;
}
void CAcmPort::BreakStateChange()
/**
* Callback for a change in the break state.
* This is called both when the break state changes because the RComm client
* wanted it to, and when the break state changes because the host asked us
* to.
* This is implemented to update our signal line flags, complete
* NotifySignalChange requests, and complete NotifyBreak requests.
*/
{
LOG_FUNC
// TODO: what if no iAcm?
// Take a copy of the current signal state for comparison later
TUint32 oldSignals = iSignals;
// grab the state of the BREAK from the ACM class that manages
// it and apply the appropriate flag state to the local iSignals
if ( iAcm && iAcm->BreakActive() )
{
LOGTEXT(_L8("\tbreak is active"));
iSignals |= KSignalBreak;
}
else
{
LOGTEXT(_L8("\tbreak is inactive"));
iSignals &= ~KSignalBreak;
}
// first tell the client (if interested, and if it's not a client-driven
// break) that there has been a BREAK event of some sort (either into or
// out of the break-active condition...
if ( iNotifyBreak && !iBreak )
{
LOGTEXT(_L8("\tcalling BreakNotifyCompleted with KErrNone"));
BreakNotifyCompleted(KErrNone);
iNotifyBreak = EFalse;
}
// and now tell the client (again if interested) the new state
// of the BREAK signal, if it has changed
if ( iNotifySignalChange
&& ( ( iNotifySignalMask & ( iSignals ^ oldSignals ) ) != 0 ) )
{
// Report correctly mapped signals that client asked for
LOGTEXT(_L8("\tcalling SignalChangeCompleted with KErrNone"));
TUint32 signals = ConvertSignals ( KBreakChanged | ( iSignals & iNotifySignalMask ) );
SignalChangeCompleted( signals, KErrNone);
iNotifySignalChange = EFalse;
}
}
void CAcmPort::NotifyFlowControlChange()
/**
* Downcall from C32. Notify a client of a change in the flow control state.
* Note that this is not supported.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
FlowControlChangeCompleted(EFlowControlOn, KErrAccessDenied);
return;
}
LOGTEXT(_L8("\t***not supported"));
FlowControlChangeCompleted(EFlowControlOn,KErrNotSupported);
}
void CAcmPort::NotifyFlowControlChangeCancel()
/**
* Downcall from C32. Cancel a pending request to be notified when the flow
* control state changes. Note that C32 actually completes the client's
* request for us.
*/
{
LOG_LINE
LOG_FUNC
}
void CAcmPort::NotifyConfigChange()
/**
* Downcall from C32. Notify a client of a change to the serial port
* configuration.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
ConfigChangeCompleted(iCommNotificationDes, KErrAccessDenied);
return;
}
if ( iNotifyConfigChange )
{
LOGTEXT(_L8("\t***in use"));
ConfigChangeCompleted(iCommNotificationDes,KErrInUse);
}
else
{
iNotifyConfigChange = ETrue;
}
}
void CAcmPort::NotifyConfigChangeCancel()
/**
* Downcall from C32. Cancel a pending request to be notified of a change to
* the serial port configuration. Note that C32 actually completes the
* client's request for us.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return;
}
iNotifyConfigChange = EFalse;
}
void CAcmPort::HostConfigChange(const TCommConfigV01& aConfig)
/**
* Callback from a "host-pushed" configuration change packet.
*
* @param aConfig New configuration data.
*/
{
LOG_FUNC
HandleConfigNotification(aConfig.iRate,
aConfig.iDataBits,
aConfig.iParity,
aConfig.iStopBits,
iCommConfig.iHandshake);
iCommConfig.iRate = aConfig.iRate;
iCommConfig.iDataBits = aConfig.iDataBits;
iCommConfig.iParity = aConfig.iParity;
iCommConfig.iStopBits = aConfig.iStopBits;
}
void CAcmPort::NotifySignalChange(TUint aSignalMask)
/**
* Downcall from C32. Notify a client of a change to the signal lines.
*/
{
LOG_LINE
LOGTEXT2(_L8(">>CAcmPort::NotifySignalChange aSignalMask=0x%08x"),
aSignalMask);
if ( iNotifySignalChange )
{
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
SignalChangeCompleted(0, KErrAccessDenied);
}
else
{
LOGTEXT(_L8("\t***in use"));
SignalChangeCompleted(0, KErrInUse);
}
}
else
{
iNotifySignalChange = ETrue;
// convert signals to internal format
// no need to filter as can notify on inputs or outputs
iNotifySignalMask = ConvertSignals(aSignalMask);
}
LOGTEXT(_L8("<<CAcmPort::NotifySignalChange"));
}
void CAcmPort::NotifySignalChangeCancel()
/**
* Downcall from C32. Cancel a pending client request to be notified about a
* change to the signal lines. Note that C32 actually completes the client's
* request for us.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return;
}
iNotifySignalChange = EFalse;
}
void CAcmPort::HostSignalChange(TBool aDtr, TBool aRts)
/**
* Callback from a "host-pushed" line state change.
*
* @param aDtr The state of the DTR signal.
* @param aRts The state of the RTS signal.
*/
{
LOGTEXT3(_L8(">>CAcmPort::HostSignalChange aDtr=%d, aRts=%d"), aDtr, aRts);
// Take a copy of the current signal state for comparison later
TUint32 oldSignals = iSignals;
if ( aDtr )
{
iSignals |= KSignalDTR;
}
else
{
iSignals &= (~KSignalDTR);
}
if ( aRts )
{
iSignals |= KSignalRTS;
}
else
{
iSignals &= (~KSignalRTS);
}
// Report the new state of the signals if they have changed
// and the mask shows the upper layer wanted to be sensitive to these signals.
if ( iNotifySignalChange )
{
// Create bitfield of changed signals that client is interested in
TUint32 changedSignals = ( iSignals ^ oldSignals ) & iNotifySignalMask;
LOGTEXT5(_L8("\tiSignals=%x, oldSignals=%x, changedSignals=%x, iNotifySignalMask=%x")
,iSignals, oldSignals, changedSignals, iNotifySignalMask);
if ( changedSignals != 0 )
{
// Mark which signals have changed
TUint32 changedSignalsMask = 0;
if ( ( changedSignals & KSignalDTR ) != 0 )
{
changedSignalsMask |= KDTRChanged;
}
if ( ( changedSignals & KSignalRTS ) != 0 )
{
changedSignalsMask |= KRTSChanged;
}
LOGTEXT2(_L8("\tchangedSignalsMask=%x"), changedSignalsMask);
// Report correctly mapped signals that client asked for
TUint32 signals = ConvertSignals ( changedSignalsMask | ( iSignals & iNotifySignalMask ) );
LOGTEXT2(_L8("\tcalling SignalChangeCompleted with KErrNone, signals=%x"), signals);
SignalChangeCompleted( signals, KErrNone);
iNotifySignalChange = EFalse;
}
}
LOGTEXT(_L8("<<CAcmPort::HostSignalChange"));
}
TInt CAcmPort::GetRole(TCommRole& aRole)
/**
* Downcall from C32. Get the role of this port unit
*
* @param aRole reference to where the role will be written to
* @return Error.
*/
{
LOG_LINE
LOG_FUNC
if ( !iAcm )
{
LOGTEXT(_L8("\t***access denied"));
return KErrAccessDenied;
}
aRole = iRole;
LOGTEXT2(_L8("\trole=%d"), aRole);
return KErrNone;
}
TInt CAcmPort::SetRole(TCommRole aRole)
/**
* Downcall from C32. Set the role of this port unit
*
* @param aRole the new role
* @return Error.
*/
{
LOG_LINE
LOGTEXT2(_L8(">>CAcmPort::SetRole aRole=%d"), aRole);
TInt ret = KErrNone;
if ( !iAcm )
{
ret = KErrAccessDenied;
}
else
{
iRole = aRole;
}
LOGTEXT2(_L8("<<CAcmPort::SetRole ret=%d"), ret);
return ret;
}
void CAcmPort::SetAcm(CCdcAcmClass* aAcm)
/**
* Called by the factory when the ACM interface for this port is about to be
* either created or destroyed.
* If it's going to be destroyed, we must cancel any currently outstanding
* requests on the reader and writer objects, complete any outstanding
* requests with KErrAccessDenied, and refuse any new requests with the same
* error until the registration port is back open again.
* If it's going to be opened, we simply remember it and use it to pass future
* client requests onto the bus.
*
* @param aAcm The new ACM (may be NULL).
*/
{
LOGTEXT3(_L8(">>CAcmPort::SetAcm aAcm = 0x%08x, iAcm = 0x%08x"), aAcm, iAcm);
if ( !aAcm )
{
// Cancel any outstanding operations on the reader and writer.
__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
iReader->ReadCancel();
__ASSERT_DEBUG(iWriter, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
iWriter->WriteCancel();
// Complete any outstanding requests
LOGTEXT(_L8("\tcompleting outstanding requests with KErrAccessDenied"));
SignalChangeCompleted(iSignals,KErrAccessDenied);
ReadCompleted(KErrAccessDenied);
WriteCompleted(KErrAccessDenied);
ConfigChangeCompleted(iCommNotificationDes,KErrAccessDenied);
NotifyDataAvailableCompleted(KErrAccessDenied);
NotifyOutputEmptyCompleted(KErrAccessDenied);
BreakNotifyCompleted(KErrAccessDenied);
FlowControlChangeCompleted(EFlowControlOn, KErrAccessDenied);
}
else
{
__ASSERT_DEBUG(!iAcm, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
aAcm->SetCallback(this);
// Set the port as the observer of break events.
aAcm->SetBreakCallback(this);
}
iAcm = aAcm;
LOGTEXT(_L8("<<CAcmPort::SetAcm"));
}
CAcmPort::~CAcmPort()
/**
* Destructor.
*/
{
LOG_FUNC
delete iReader;
delete iWriter;
// Remove this as a sink for the ACM class to call back about host-pushed
// changes.
if ( iAcm )
{
LOGTEXT(_L8("\tiAcm exists- calling SetCallback(NULL)"));
iAcm->SetCallback(NULL);
}
LOGTEXT(_L8("\tcalling AcmPortClosed on observer"));
iObserver.AcmPortClosed(iUnit);
}
//
// End of file